├── opensearchpy ├── py.typed ├── .pylintrc ├── plugins │ ├── __init__.py │ └── ubi.py ├── _async │ ├── helpers │ │ ├── __init__.py │ │ └── test.py │ ├── plugins │ │ ├── __init__.py │ │ └── ubi.py │ ├── __init__.py │ ├── compat.py │ ├── client │ │ ├── remote.py │ │ ├── utils.py │ │ ├── client.py │ │ ├── insights.py │ │ └── features.py │ └── _extra_imports.py ├── metrics │ ├── __init__.py │ ├── metrics.py │ ├── metrics_none.py │ └── metrics_events.py ├── _version.py ├── client │ ├── remote.py │ ├── client.py │ ├── insights.py │ └── features.py ├── connection │ ├── __init__.py │ └── pooling.py ├── helpers │ ├── asyncsigner.py │ ├── errors.py │ ├── __init__.py │ └── response │ │ └── hit.py └── compat.py ├── docs ├── source │ ├── _templates │ │ └── .gitkeep │ ├── genindex.md │ ├── imgs │ │ └── OpenSearch.svg │ ├── api-ref │ │ ├── helpers │ │ │ ├── aggs.md │ │ │ ├── field.md │ │ │ ├── index.md │ │ │ ├── query.md │ │ │ ├── search.md │ │ │ ├── analysis.md │ │ │ ├── document.md │ │ │ ├── mapping.md │ │ │ ├── wrappers.md │ │ │ ├── function.md │ │ │ ├── faceted_search.md │ │ │ └── update_by_query.md │ │ ├── transport.md │ │ ├── serializer.md │ │ ├── clients │ │ │ ├── http_client.md │ │ │ ├── nodes_client.md │ │ │ ├── tasks_client.md │ │ │ ├── cluster_client.md │ │ │ ├── indices_client.md │ │ │ ├── ingest_client.md │ │ │ ├── remote_client.md │ │ │ ├── features_client.md │ │ │ ├── security_client.md │ │ │ ├── snapshot_client.md │ │ │ ├── cat_client.md │ │ │ ├── dangling_indices_client.md │ │ │ └── opensearch_client.md │ │ ├── plugins │ │ │ ├── alerting_plugin.md │ │ │ └── index_management_plugin.md │ │ ├── plugins.md │ │ ├── metrics.md │ │ ├── connection_pool.md │ │ ├── helpers.md │ │ ├── connection.md │ │ ├── client.md │ │ └── exceptions.md │ ├── api-ref.md │ ├── _static │ │ └── css │ │ │ └── custom.css │ └── index.md ├── Makefile └── make.bat ├── benchmarks ├── poetry.toml ├── pyproject.toml ├── bench_sync_async.py └── thread_with_return_value.py ├── samples ├── poetry.toml ├── snapshot │ ├── README.md │ ├── Dockerfile │ └── snapshot_sample.py ├── pyproject.toml ├── aws │ └── README.md ├── README.md ├── security │ ├── users.py │ └── roles.py ├── hello │ ├── unicode.py │ ├── unicode_async.py │ └── hello.py ├── bulk │ ├── bulk_array.py │ └── bulk_ld.py ├── json │ └── json_hello.py └── knn │ └── knn_basics.py ├── .github ├── workflows │ ├── .lychee.excludes │ ├── delete_backport_branch.yml │ ├── unified-release.yml │ ├── add-untriaged.yml │ ├── changelog_verifier.yml │ ├── links.yml │ ├── docs.yml │ ├── backport.yml │ ├── integration.yml │ ├── dependabot_pr.yml │ ├── test.yml │ ├── ci.yml │ ├── update_api.yml │ └── release-drafter.yml ├── codecov.yml ├── CODEOWNERS └── dependabot.yml ├── .dockerignore ├── utils ├── templates │ ├── overrides │ │ ├── __init__ │ │ │ ├── get │ │ │ ├── delete │ │ │ ├── exists │ │ │ ├── ping │ │ │ ├── index │ │ │ ├── exists_source │ │ │ ├── get_source │ │ │ ├── create │ │ │ ├── explain │ │ │ ├── update │ │ │ ├── mtermvectors │ │ │ ├── termvectors │ │ │ ├── scroll │ │ │ └── clear_scroll │ │ ├── indices │ │ │ ├── put_alias │ │ │ ├── put_mapping │ │ │ ├── put_mapping.json │ │ │ └── put_alias-func_params │ │ ├── cluster │ │ │ ├── state │ │ │ └── stats │ │ └── tasks │ │ │ └── get │ ├── url │ ├── substitutions │ ├── required │ ├── func_params │ └── base ├── generated_file_headers.txt └── changelog_updater.py ├── AUTHORS ├── .readthedocs.yml ├── .coveragerc ├── .ci ├── test-matrix.yml ├── opensearch │ └── Dockerfile ├── Dockerfile.client ├── functions │ ├── wait-for-container.sh │ ├── imports.sh │ └── cleanup.sh └── run-tests ├── SECURITY.md ├── test_opensearchpy ├── test_types │ └── README.md ├── test_server_secured │ ├── __init__.py │ └── test_clients.py ├── test_async │ ├── test_server_secured │ │ └── __init__.py │ ├── test_plugins_client.py │ ├── __init__.py │ ├── test_helpers │ │ └── __init__.py │ ├── test_server │ │ ├── test_helpers │ │ │ ├── __init__.py │ │ │ └── test_update_by_query.py │ │ ├── test_plugins │ │ │ └── __init__.py │ │ ├── __init__.py │ │ └── conftest.py │ └── test_http.py ├── .pylintrc ├── test_client │ ├── test_remote_store.py │ ├── test_plugins │ │ ├── test_plugins_client.py │ │ ├── __init__.py │ │ └── test_index_management.py │ ├── __init__.py │ ├── test_point_in_time.py │ ├── test_requests.py │ ├── test_search_pipeline.py │ ├── test_urllib3.py │ ├── test_http.py │ ├── test_cluster.py │ └── test_indices.py ├── __init__.py ├── test_helpers │ └── __init__.py ├── test_connection │ └── __init__.py ├── test_server │ ├── test_helpers │ │ ├── __init__.py │ │ ├── test_count.py │ │ └── test_analysis.py │ ├── test_plugins │ │ └── __init__.py │ └── __init__.py ├── test_exceptions.py └── test_http_server.py ├── NOTICE.txt ├── MANIFEST.in ├── .whitesource ├── setup.cfg ├── .pylintrc ├── dev-requirements.txt ├── Makefile ├── ADMINS.md ├── guides ├── proxy.md ├── point_in_time.md ├── dsl.md ├── plugins │ ├── security.md │ └── index_management.md ├── json.md └── ssl.md ├── COMPATIBILITY.md ├── UPGRADING.md ├── MAINTAINERS.md └── CODE_OF_CONDUCT.md /opensearchpy/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/source/_templates/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/source/genindex.md: -------------------------------------------------------------------------------- 1 | # Index 2 | -------------------------------------------------------------------------------- /benchmarks/poetry.toml: -------------------------------------------------------------------------------- 1 | [virtualenvs] 2 | create = true -------------------------------------------------------------------------------- /samples/poetry.toml: -------------------------------------------------------------------------------- 1 | [virtualenvs] 2 | create = true -------------------------------------------------------------------------------- /docs/source/imgs/OpenSearch.svg: -------------------------------------------------------------------------------- 1 | ../../../OpenSearch.svg -------------------------------------------------------------------------------- /.github/workflows/.lychee.excludes: -------------------------------------------------------------------------------- 1 | file:///github/workspace/* 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | docs 2 | example 3 | venv 4 | .tox 5 | .nox 6 | .*_cache 7 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | threshold: 0.1% -------------------------------------------------------------------------------- /docs/source/api-ref/helpers/aggs.md: -------------------------------------------------------------------------------- 1 | # aggs 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.helpers.aggs.Agg 5 | ``` -------------------------------------------------------------------------------- /docs/source/api-ref/transport.md: -------------------------------------------------------------------------------- 1 | # transport 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.Transport 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/source/api-ref/helpers/field.md: -------------------------------------------------------------------------------- 1 | # field 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.helpers.field.Field 5 | ``` -------------------------------------------------------------------------------- /docs/source/api-ref/helpers/index.md: -------------------------------------------------------------------------------- 1 | # index 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.helpers.index.Index 5 | ``` -------------------------------------------------------------------------------- /docs/source/api-ref/helpers/query.md: -------------------------------------------------------------------------------- 1 | # query 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.helpers.query.Query 5 | ``` -------------------------------------------------------------------------------- /docs/source/api-ref/helpers/search.md: -------------------------------------------------------------------------------- 1 | # search 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.helpers.search.Search 5 | ``` -------------------------------------------------------------------------------- /docs/source/api-ref/serializer.md: -------------------------------------------------------------------------------- 1 | # serializer 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.JSONSerializer 5 | ``` 6 | 7 | -------------------------------------------------------------------------------- /utils/templates/overrides/__init__/get: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | {{ super()|trim }} 4 | {% endblock %} -------------------------------------------------------------------------------- /docs/source/api-ref/helpers/analysis.md: -------------------------------------------------------------------------------- 1 | # analysis 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.helpers.analysis.Analyzer 5 | ``` -------------------------------------------------------------------------------- /docs/source/api-ref/helpers/document.md: -------------------------------------------------------------------------------- 1 | # document 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.helpers.document.Document 5 | ``` -------------------------------------------------------------------------------- /docs/source/api-ref/helpers/mapping.md: -------------------------------------------------------------------------------- 1 | # mapping 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.helpers.mapping.Mapping 5 | ``` -------------------------------------------------------------------------------- /docs/source/api-ref/helpers/wrappers.md: -------------------------------------------------------------------------------- 1 | # wrappers 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.helpers.wrappers.Range 5 | ``` -------------------------------------------------------------------------------- /utils/templates/overrides/__init__/delete: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | {{ super()|trim }} 4 | {% endblock %} -------------------------------------------------------------------------------- /utils/templates/overrides/__init__/exists: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | {{ super()|trim }} 4 | {% endblock %} -------------------------------------------------------------------------------- /docs/source/api-ref/helpers/function.md: -------------------------------------------------------------------------------- 1 | # function 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.helpers.function.ScoreFunction 5 | ``` -------------------------------------------------------------------------------- /utils/templates/url: -------------------------------------------------------------------------------- 1 | {% if api.url_parts.0 %}_make_path({{ api.url_parts.1|join(", ")}}){% else %}{{ api.url_parts.1|tojson }}{% endif %} 2 | -------------------------------------------------------------------------------- /docs/source/api-ref/clients/http_client.md: -------------------------------------------------------------------------------- 1 | # Http Client 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.client.http.HttpClient 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/source/api-ref/clients/nodes_client.md: -------------------------------------------------------------------------------- 1 | # Nodes Client 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.client.nodes.NodesClient 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/source/api-ref/clients/tasks_client.md: -------------------------------------------------------------------------------- 1 | # Tasks Client 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.client.tasks.TasksClient 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/source/api-ref/clients/cluster_client.md: -------------------------------------------------------------------------------- 1 | # Cluster Client 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.client.cluster.ClusterClient 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/source/api-ref/clients/indices_client.md: -------------------------------------------------------------------------------- 1 | # Indices Client 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.client.indices.IndicesClient 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/source/api-ref/clients/ingest_client.md: -------------------------------------------------------------------------------- 1 | # Ingest Client 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.client.ingest.IngestClient 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/source/api-ref/clients/remote_client.md: -------------------------------------------------------------------------------- 1 | # Remote Client 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.client.remote.RemoteClient 5 | ``` 6 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | For a list of all our amazing authors please see the contributors page: 2 | https://github.com/opensearch-project/opensearch-py/graphs/contributors 3 | -------------------------------------------------------------------------------- /docs/source/api-ref/clients/features_client.md: -------------------------------------------------------------------------------- 1 | # Features Client 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.client.features.FeaturesClient 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/source/api-ref/clients/security_client.md: -------------------------------------------------------------------------------- 1 | # Security Client 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.client.security.SecurityClient 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/source/api-ref/clients/snapshot_client.md: -------------------------------------------------------------------------------- 1 | # Snapshot Client 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.client.snapshot.SnapshotClient 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/source/api-ref/helpers/faceted_search.md: -------------------------------------------------------------------------------- 1 | # faceted_search 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.helpers.faceted_search.FacetedSearch 5 | ``` -------------------------------------------------------------------------------- /docs/source/api-ref/plugins/alerting_plugin.md: -------------------------------------------------------------------------------- 1 | # Alerting Plugin 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.plugins.alerting.AlertingClient 5 | ``` 6 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | python: 4 | version: 3.10 5 | install: 6 | - method: pip 7 | path: . 8 | - requirements: dev-requirements.txt 9 | -------------------------------------------------------------------------------- /docs/source/api-ref/helpers/update_by_query.md: -------------------------------------------------------------------------------- 1 | # update_by_query 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.helpers.update_by_query.UpdateByQuery 5 | ``` -------------------------------------------------------------------------------- /utils/templates/overrides/indices/put_alias: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block func_params %}{% include "overrides/indices/put_alias-func_params" %}{% endblock %} 3 | -------------------------------------------------------------------------------- /docs/source/api-ref/clients/cat_client.md: -------------------------------------------------------------------------------- 1 | # Compact and aligned text (CAT) Client 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.client.cat.CatClient 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/source/api-ref.md: -------------------------------------------------------------------------------- 1 | # API Reference 2 | 3 | ```{toctree} 4 | --- 5 | glob: 6 | titlesonly: 7 | maxdepth: 2 8 | --- 9 | 10 | api-ref/client 11 | api-ref/* 12 | ``` 13 | -------------------------------------------------------------------------------- /docs/source/api-ref/clients/dangling_indices_client.md: -------------------------------------------------------------------------------- 1 | # Dangling Indices Client 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.client.dangling_indices.DanglingIndicesClient 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/source/api-ref/plugins/index_management_plugin.md: -------------------------------------------------------------------------------- 1 | # Index Management Plugin 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.plugins.index_management.IndexManagementClient 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/source/api-ref/plugins.md: -------------------------------------------------------------------------------- 1 | # Plugins 2 | 3 | ```{toctree} 4 | --- 5 | glob: 6 | titlesonly: 7 | maxdepth: 1 8 | --- 9 | 10 | plugins/alerting_plugin 11 | plugins/index_management_plugin 12 | ``` -------------------------------------------------------------------------------- /utils/templates/overrides/indices/put_mapping: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | if index in SKIP_IN_PATH: 4 | index = "_all" 5 | 6 | {{ super()|trim }} 7 | {% endblock %} 8 | 9 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | */python?.?/* 4 | */lib-python/?.?/*.py 5 | */lib_pypy/* 6 | */site-packages/* 7 | *.egg/* 8 | test_opensearch/* 9 | opensearch/connection/esthrift/* 10 | -------------------------------------------------------------------------------- /utils/templates/overrides/__init__/ping: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | try: 4 | {{ super()|trim }} 5 | except TransportError: 6 | return False 7 | {% endblock %} 8 | 9 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This should match the team set up in https://github.com/orgs/opensearch-project/teams and include any additional contributors 2 | * @VachaShah @harshavamsi @axeoman @Shephalimittal @saimedhi @florianvazelle 3 | -------------------------------------------------------------------------------- /utils/templates/overrides/cluster/state: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | if index and metric in SKIP_IN_PATH: 4 | metric = "_all" 5 | 6 | {{ super()|trim }} 7 | {% endblock %} 8 | 9 | -------------------------------------------------------------------------------- /docs/source/api-ref/clients/opensearch_client.md: -------------------------------------------------------------------------------- 1 | # OpenSearch Client 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.OpenSearch 5 | ``` 6 | 7 | ```{eval-rst} 8 | .. autoclass:: opensearchpy.AsyncOpenSearch 9 | ``` 10 | -------------------------------------------------------------------------------- /.ci/test-matrix.yml: -------------------------------------------------------------------------------- 1 | TEST_SUITE: 2 | - oss 3 | 4 | PYTHON_VERSION: 5 | - "3.10" 6 | - "3.11" 7 | - "3.12" 8 | 9 | PYTHON_CONNECTION_CLASS: 10 | - Urllib3HttpConnection 11 | - RequestsHttpConnection 12 | 13 | exclude: ~ 14 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Reporting a Vulnerability 2 | 3 | If you discover a potential security issue in this project we ask that you notify OpenSearch Security directly via email to security@opensearch.org. Please do **not** create a public GitHub issue. 4 | -------------------------------------------------------------------------------- /samples/snapshot/README.md: -------------------------------------------------------------------------------- 1 | Run this sample as follows. 2 | 3 | ``` 4 | cd samples 5 | docker run --rm -p 9200:9200 -p 9600:9600 -e "discovery.type=single-node" -it $(docker build -q snapshot ) 6 | poetry install 7 | poetry run python snapshot/snapshot_sample.py 8 | ``` 9 | -------------------------------------------------------------------------------- /utils/templates/overrides/__init__/index: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | return await self.transport.perform_request("POST" if id in SKIP_IN_PATH else "PUT", {% include "url" %}, params=params, headers=headers, body=body) 4 | {% endblock %} 5 | 6 | -------------------------------------------------------------------------------- /utils/templates/overrides/__init__/exists_source: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | path = _make_path(index, "_source", id) 4 | 5 | return await self.transport.perform_request("{{ api.method }}", path, params=params, headers=headers) 6 | {% endblock %} -------------------------------------------------------------------------------- /utils/templates/overrides/__init__/get_source: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | path = _make_path(index, "_source", id) 4 | 5 | return await self.transport.perform_request("{{ api.method }}", path, params=params, headers=headers) 6 | {% endblock %} -------------------------------------------------------------------------------- /docs/source/api-ref/metrics.md: -------------------------------------------------------------------------------- 1 | # metrics 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.Metrics 5 | ``` 6 | 7 | ```{eval-rst} 8 | .. autoclass:: opensearchpy.MetricsEvents 9 | ``` 10 | 11 | ```{eval-rst} 12 | .. autoclass:: opensearchpy.MetricsNone 13 | ``` 14 | 15 | -------------------------------------------------------------------------------- /utils/templates/overrides/__init__/create: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | path = _make_path(index, "_create", id) 4 | 5 | return await self.transport.perform_request("{{ api.method }}", path, params=params, headers=headers, body=body) 6 | {% endblock %} -------------------------------------------------------------------------------- /utils/templates/overrides/__init__/explain: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | path = _make_path(index, "_explain", id) 4 | 5 | return await self.transport.perform_request("{{ api.method }}", path, params=params, headers=headers, body=body) 6 | {% endblock %} -------------------------------------------------------------------------------- /utils/templates/overrides/__init__/update: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | path = _make_path(index, "_update", id) 4 | 5 | return await self.transport.perform_request("{{ api.method }}", path, params=params, headers=headers, body=body) 6 | {% endblock %} -------------------------------------------------------------------------------- /test_opensearchpy/test_types/README.md: -------------------------------------------------------------------------------- 1 | # Type Hints 2 | 3 | All of these scripts are used to test the type hinting 4 | distributed with the `opensearch` package. 5 | These scripts simulate normal usage of the client and are run 6 | through `mypy --strict` as a part of continuous integration. 7 | -------------------------------------------------------------------------------- /utils/templates/overrides/__init__/mtermvectors: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | path = _make_path(index, "_mtermvectors") 4 | 5 | return await self.transport.perform_request("{{ api.method }}", path, params=params, headers=headers, body=body) 6 | {% endblock %} -------------------------------------------------------------------------------- /utils/templates/overrides/__init__/termvectors: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | path = _make_path(index, "_termvectors", id) 4 | 5 | return await self.transport.perform_request("{{ api.method }}", path, params=params, headers=headers, body=body) 6 | {% endblock %} -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | OpenSearch (https://opensearch.org/) 2 | Copyright OpenSearch Contributors 3 | 4 | This product includes software developed by 5 | Elasticsearch (http://www.elastic.co). 6 | 7 | This product includes software developed by The Apache Software 8 | Foundation (http://www.apache.org/). 9 | -------------------------------------------------------------------------------- /docs/source/api-ref/connection_pool.md: -------------------------------------------------------------------------------- 1 | # connection_pool 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.ConnectionPool 5 | ``` 6 | 7 | ```{eval-rst} 8 | .. autoclass:: opensearchpy.ConnectionSelector 9 | ``` 10 | 11 | ```{eval-rst} 12 | .. autoclass:: opensearchpy.RoundRobinSelector 13 | ``` 14 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS 2 | include Changelog.rst 3 | include CONTRIBUTING.md 4 | include LICENSE 5 | include MANIFEST.in 6 | include README.md 7 | include setup.py 8 | recursive-include opensearch* py.typed 9 | 10 | prune test_opensearch 11 | recursive-exclude * __pycache__ 12 | recursive-exclude * *.py[co] 13 | -------------------------------------------------------------------------------- /opensearchpy/.pylintrc: -------------------------------------------------------------------------------- 1 | [MESSAGES CONTROL] 2 | disable=all 3 | enable=line-too-long, 4 | invalid-name, 5 | pointless-statement, 6 | unspecified-encoding, 7 | unnecessary-dunder-call, 8 | assignment-from-no-return, 9 | unused-variable 10 | max-line-length=240 11 | good-names-rgxs=^[_a-z][_a-z0-9]?$ -------------------------------------------------------------------------------- /opensearchpy/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | -------------------------------------------------------------------------------- /opensearchpy/_async/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | -------------------------------------------------------------------------------- /opensearchpy/_async/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | -------------------------------------------------------------------------------- /test_opensearchpy/test_server_secured/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | -------------------------------------------------------------------------------- /.ci/opensearch/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG OPENSEARCH_VERSION 2 | FROM opensearchproject/opensearch:$OPENSEARCH_VERSION 3 | 4 | ARG opensearch_path=/usr/share/opensearch 5 | ARG opensearch_yml=$opensearch_path/config/opensearch.yml 6 | 7 | ARG SECURE_INTEGRATION 8 | RUN if [ "$SECURE_INTEGRATION" != "true" ] ; then echo "plugins.security.disabled: true" >> $opensearch_yml; fi 9 | -------------------------------------------------------------------------------- /samples/snapshot/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM opensearchproject/opensearch:2.11.0 2 | 3 | ARG OPENSEARCH_HOME=/usr/share/opensearch 4 | ARG UID=1000 5 | ARG GID=1000 6 | 7 | RUN echo 'path.repo: ["/usr/share/opensearch/backups"]' >> $OPENSEARCH_HOME/config/opensearch.yml 8 | RUN mkdir -p $OPENSEARCH_HOME/backups 9 | RUN chown -Rv $UID:$GID $OPENSEARCH_HOME/backups 10 | -------------------------------------------------------------------------------- /test_opensearchpy/test_async/test_server_secured/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "scanSettings": { 3 | "configMode": "AUTO", 4 | "configExternalURL": "", 5 | "projectToken": "", 6 | "baseBranches": [] 7 | }, 8 | "checkRunSettings": { 9 | "vulnerableCheckRunConclusionLevel": "failure", 10 | "displayMode": "diff" 11 | }, 12 | "issueSettings": { 13 | "minSeverityLevel": "LOW" 14 | } 15 | } -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_rpm] 2 | requires = python python-urllib3 3 | 4 | [flake8] 5 | ignore = E203, E266, E501, W503 6 | max-line-length = 240 7 | 8 | [tool:pytest] 9 | junit_family=legacy 10 | asyncio_mode=auto 11 | 12 | [isort] 13 | profile=black 14 | 15 | [black] 16 | max-line-length = 240 17 | target-version = 'py38' 18 | 19 | [mypy] 20 | ignore_missing_imports=True 21 | -------------------------------------------------------------------------------- /utils/templates/substitutions: -------------------------------------------------------------------------------- 1 | {% for p, info in api.params %} 2 | {% if p in substitutions and p not in api.url_parts.1 %} 3 | # {{ substitutions[p] }} is a reserved word so it cannot be used, use {{ p }} instead 4 | if "{{ p }}" in params: 5 | params["{{ substitutions[p] }}"] = params.pop("{{ p }}") 6 | 7 | {% endif %} 8 | {% endfor %} 9 | 10 | -------------------------------------------------------------------------------- /docs/source/api-ref/helpers.md: -------------------------------------------------------------------------------- 1 | # helpers 2 | 3 | ```{toctree} 4 | --- 5 | glob: 6 | titlesonly: 7 | maxdepth: 1 8 | --- 9 | 10 | helpers/aggs 11 | helpers/analysis 12 | helpers/document 13 | helpers/faceted_search 14 | helpers/field 15 | helpers/function 16 | helpers/index 17 | helpers/mapping 18 | helpers/query 19 | helpers/search 20 | helpers/update_by_query 21 | helpers/wrappers 22 | 23 | ``` -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MESSAGES CONTROL] 2 | disable=all 3 | enable=line-too-long, 4 | invalid-name, 5 | pointless-statement, 6 | unspecified-encoding, 7 | missing-function-docstring, 8 | missing-param-doc, 9 | differing-param-doc, 10 | unnecessary-dunder-call, 11 | assignment-from-no-return, 12 | unused-variable 13 | max-line-length=240 14 | good-names-rgxs=^[_a-z][_a-z0-9]?$ 15 | 16 | -------------------------------------------------------------------------------- /test_opensearchpy/.pylintrc: -------------------------------------------------------------------------------- 1 | [MESSAGES CONTROL] 2 | disable=all 3 | enable=line-too-long, 4 | invalid-name, 5 | pointless-statement, 6 | unspecified-encoding, 7 | missing-param-doc, 8 | differing-param-doc, 9 | unnecessary-dunder-call, 10 | assignment-from-no-return, 11 | unused-variable 12 | max-line-length=240 13 | good-names-rgxs=^[_a-z][_a-z0-9]?$ -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | requests>=2, <3 2 | pytest 3 | pytest-cov 4 | coverage 5 | sphinx<8.2 6 | sphinx_rtd_theme 7 | jinja2 8 | pytz 9 | deepmerge 10 | Events 11 | setuptools==71.1.0 12 | 13 | numpy; python_version<="3.12" 14 | pandas; python_version<="3.12" 15 | 16 | pyyaml>=5.4 17 | 18 | isort 19 | black>=24.3.0 20 | twine 21 | 22 | # Requirements for testing [async] extra 23 | aiohttp>=3.10.11, <4 24 | pytest-asyncio<=1.3.0 25 | unasync 26 | -------------------------------------------------------------------------------- /.ci/Dockerfile.client: -------------------------------------------------------------------------------- 1 | ARG PYTHON_VERSION=3.10 2 | FROM python:${PYTHON_VERSION} 3 | 4 | WORKDIR /code/opensearch-py 5 | COPY dev-requirements.txt . 6 | RUN python -m pip install \ 7 | -U --no-cache-dir \ 8 | --disable-pip-version-check \ 9 | pip \ 10 | && python -m pip install \ 11 | --no-cache-dir \ 12 | --disable-pip-version-check \ 13 | -r dev-requirements.txt 14 | 15 | COPY . . 16 | RUN python -m pip install -e . 17 | -------------------------------------------------------------------------------- /docs/source/api-ref/connection.md: -------------------------------------------------------------------------------- 1 | # Connection Types 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.Connection 5 | ``` 6 | 7 | ```{eval-rst} 8 | .. autoclass:: opensearchpy.RequestsHttpConnection 9 | ``` 10 | 11 | ```{eval-rst} 12 | .. autoclass:: opensearchpy.Urllib3HttpConnection 13 | ``` 14 | 15 | ```{eval-rst} 16 | .. autoclass:: opensearchpy.AIOHttpConnection 17 | ``` 18 | 19 | ```{eval-rst} 20 | .. autoclass:: opensearchpy.connections 21 | ``` -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | updates: 2 | - directory: / 3 | open-pull-requests-limit: 3 4 | package-ecosystem: pip 5 | schedule: 6 | interval: weekly 7 | labels: 8 | - "dependabot" 9 | - "dependencies" 10 | - directory: / 11 | open-pull-requests-limit: 3 12 | package-ecosystem: github-actions 13 | schedule: 14 | interval: weekly 15 | labels: 16 | - "dependabot" 17 | - "dependencies" 18 | version: 2 19 | -------------------------------------------------------------------------------- /.github/workflows/delete_backport_branch.yml: -------------------------------------------------------------------------------- 1 | name: Delete merged branch of the backport PRs 2 | on: 3 | pull_request: 4 | types: 5 | - closed 6 | 7 | jobs: 8 | delete-branch: 9 | runs-on: ubuntu-latest 10 | if: startsWith(github.event.pull_request.head.ref,'backport/') 11 | steps: 12 | - name: Delete merged branch 13 | uses: SvanBoxel/delete-merged-branch@main 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /samples/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "package" 3 | version = "0.1.0" 4 | description = "OpenSearch samples." 5 | authors = ["Daniel Doubrovkine "] 6 | license = "Apache 2.0" 7 | readme = "README.md" 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.10" 11 | opensearch-py = { path = "../", extras=["async"], develop = true } 12 | boto3 = "^1.28" 13 | 14 | [build-system] 15 | requires = ["poetry-core"] 16 | build-backend = "poetry.core.masonry.api" 17 | -------------------------------------------------------------------------------- /utils/templates/required: -------------------------------------------------------------------------------- 1 | {% if api.required_parts.1 %} 2 | for param in ({{ api.required_parts|join(", ")}}): 3 | if param in SKIP_IN_PATH: 4 | raise ValueError("Empty value passed for a required argument.") 5 | 6 | {% elif api.required_parts %} 7 | if {{ api.required_parts.0 }} in SKIP_IN_PATH: 8 | raise ValueError("Empty value passed for a required argument '{{ api.required_parts.0 }}'.") 9 | 10 | {% endif %} 11 | 12 | -------------------------------------------------------------------------------- /.github/workflows/unified-release.yml: -------------------------------------------------------------------------------- 1 | name: Unified Release 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | assemble: 7 | name: Assemble 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | stack_version: ["3.1.0"] 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v6 17 | - run: "./.ci/make.sh assemble ${{ matrix.stack_version }}" 18 | name: Assemble ${{ matrix.stack_version }} 19 | -------------------------------------------------------------------------------- /utils/templates/overrides/tasks/get: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | if task_id in SKIP_IN_PATH: 4 | warnings.warn( 5 | "Calling client.tasks.get() without a task_id is deprecated " 6 | "and will be removed in a future version. Use client.tasks.list() instead.", 7 | category=DeprecationWarning, 8 | stacklevel=3, 9 | ) 10 | 11 | {{ super()|trim }} 12 | {% endblock %} 13 | 14 | -------------------------------------------------------------------------------- /benchmarks/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "opensearch-py-benchmarks" 3 | version = "0.1.0" 4 | description = "OpenSearch Python client benchmarks." 5 | authors = ["Daniel Doubrovkine "] 6 | license = "Apache 2.0" 7 | readme = "README.md" 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.10" 11 | opensearch-py = { path = "..", develop=true, extras=["async"] } 12 | richbench = "*" 13 | 14 | [build-system] 15 | requires = ["poetry-core"] 16 | build-backend = "poetry.core.masonry.api" 17 | -------------------------------------------------------------------------------- /benchmarks/bench_sync_async.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # The OpenSearch Contributors require contributions made to 6 | # this file be licensed under the Apache-2.0 license or a 7 | # compatible open source license. 8 | # 9 | # Modifications Copyright OpenSearch Contributors. See 10 | # GitHub history for details. 11 | 12 | 13 | import bench_async 14 | import bench_sync 15 | 16 | __benchmarks__ = [(bench_sync.test_32, bench_async.test_8, "sync vs. async (8)")] 17 | -------------------------------------------------------------------------------- /docs/source/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | /* 2 | .wy-nav-top: top box in mobile view 3 | .wy-side-nav-search: top left box in nav bar 4 | .wy-side-nav-search > a:hover: no hover highlight 5 | */ 6 | .wy-nav-top, 7 | .wy-side-nav-search, 8 | .wy-side-nav-search > a:hover { 9 | background-color: #2980b9; 10 | } 11 | 12 | /* broader content area */ 13 | .wy-nav-content { 14 | max-width: 1000px; 15 | } 16 | 17 | /* top left project name -> bigger */ 18 | .wy-side-nav-search > a.icon.icon-home { 19 | font-size: 160%; 20 | } 21 | -------------------------------------------------------------------------------- /utils/templates/overrides/cluster/stats: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block func_params %} 3 | node_id: Any = None, 4 | params: Any = None, 5 | headers: Any = None, 6 | metric: Any = None, 7 | index_metric: Any = None, 8 | {% endblock %} 9 | {% block request %} 10 | return await self.transport.perform_request("{{ api.method }}", "/_cluster/stats" if node_id in SKIP_IN_PATH else _make_path("_cluster", "stats", metric, index_metric, "nodes", node_id), params=params, headers=headers) 11 | {% endblock%} 12 | 13 | -------------------------------------------------------------------------------- /utils/generated_file_headers.txt: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------------------ 2 | # THIS CODE IS AUTOMATICALLY GENERATED AND MANUAL EDITS WILL BE LOST 3 | # 4 | # To contribute, kindly make modifications in the opensearch-py client generator 5 | # or in the OpenSearch API specification, and run `nox -rs generate`. See DEVELOPER_GUIDE.md 6 | # and https://github.com/opensearch-project/opensearch-api-specification for details. 7 | # -----------------------------------------------------------------------------------------+ 8 | -------------------------------------------------------------------------------- /utils/templates/overrides/__init__/scroll: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | if scroll_id in SKIP_IN_PATH and body in SKIP_IN_PATH: 4 | raise ValueError("You need to supply scroll_id or body.") 5 | elif scroll_id and not body: 6 | body = {"scroll_id": scroll_id} 7 | elif scroll_id: 8 | params["scroll_id"] = scroll_id 9 | 10 | return await self.transport.perform_request("{{ api.method }}", "/_search/scroll", params=params, headers=headers, body=body) 11 | {% endblock %} 12 | 13 | -------------------------------------------------------------------------------- /opensearchpy/metrics/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | from .metrics import Metrics 11 | from .metrics_events import MetricsEvents 12 | from .metrics_none import MetricsNone 13 | 14 | __all__ = [ 15 | "Metrics", 16 | "MetricsEvents", 17 | "MetricsNone", 18 | ] 19 | -------------------------------------------------------------------------------- /utils/templates/overrides/__init__/clear_scroll: -------------------------------------------------------------------------------- 1 | {% extends "base" %} 2 | {% block request %} 3 | if scroll_id in SKIP_IN_PATH and body in SKIP_IN_PATH: 4 | raise ValueError("You need to supply scroll_id or body.") 5 | elif scroll_id and not body: 6 | body = {"scroll_id": [scroll_id]} 7 | elif scroll_id: 8 | params["scroll_id"] = scroll_id 9 | 10 | return await self.transport.perform_request("{{ api.method }}", "/_search/scroll", params=params, headers=headers, body=body) 11 | {% endblock %} 12 | 13 | -------------------------------------------------------------------------------- /.github/workflows/add-untriaged.yml: -------------------------------------------------------------------------------- 1 | name: Apply 'untriaged' label during issue lifecycle 2 | 3 | on: 4 | issues: 5 | types: [opened, reopened, transferred] 6 | 7 | jobs: 8 | apply-label: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/github-script@v8 12 | with: 13 | script: | 14 | github.rest.issues.addLabels({ 15 | issue_number: context.issue.number, 16 | owner: context.repo.owner, 17 | repo: context.repo.repo, 18 | labels: ['untriaged'] 19 | }) 20 | -------------------------------------------------------------------------------- /utils/templates/func_params: -------------------------------------------------------------------------------- 1 | {% for p, info in api.all_parts.items() %} 2 | {% if info.required %}{{ p }}: {{ info.type }}, {% endif %} 3 | {% endfor %} 4 | 5 | {% if api.body %} 6 | body{% if not api.body.required %}: Any=None{% else %}: Any{% endif %}, 7 | {% endif %} 8 | 9 | {% for p, info in api.all_parts.items() %} 10 | {% if not info.required and not info.type == 'Any' %}{{ p }}: Optional[{{ info.type }}]=None, {% endif %} 11 | {% if not info.required and info.type == 'Any' %}{{ p }}: {{ info.type }}=None, {% endif %} 12 | {% endfor %} 13 | 14 | params: Any=None, 15 | headers: Any=None, -------------------------------------------------------------------------------- /.github/workflows/changelog_verifier.yml: -------------------------------------------------------------------------------- 1 | name: "Changelog Verifier" 2 | on: 3 | pull_request: 4 | types: [opened, edited, review_requested, synchronize, reopened, ready_for_review, labeled, unlabeled] 5 | 6 | jobs: 7 | # Enforces the update of a changelog file on every pull request 8 | verify-changelog: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v6 12 | with: 13 | token: ${{ secrets.GITHUB_TOKEN }} 14 | ref: ${{ github.event.pull_request.head.sha }} 15 | 16 | - uses: dangoslen/changelog-enforcer@v3 17 | with: 18 | skipLabels: "autocut, skip-changelog" -------------------------------------------------------------------------------- /test_opensearchpy/test_client/test_remote_store.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | from test_opensearchpy.test_cases import OpenSearchTestCase 10 | 11 | 12 | class TestRemoteStore(OpenSearchTestCase): 13 | def test_remote_store_restore(self) -> None: 14 | self.client.remote_store.restore(body=["index-1"]) 15 | self.assert_url_called("POST", "/_remotestore/_restore") 16 | -------------------------------------------------------------------------------- /.github/workflows/links.yml: -------------------------------------------------------------------------------- 1 | name: Link Checker 2 | on: 3 | push: 4 | branches: 5 | - "*" 6 | pull_request: 7 | branches: 8 | - "*" 9 | 10 | jobs: 11 | linkchecker: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v6 17 | - name: lychee Link Checker 18 | id: lychee 19 | uses: lycheeverse/lychee-action@v2.7.0 20 | with: 21 | args: --accept=200,403,429 "**/*.html" "**/*.md" "**/*.txt" "**/*.json" --exclude-file ".github/workflows/.lychee.excludes" 22 | fail: true 23 | env: 24 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 25 | 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | clean: 2 | docker-compose down --remove-orphans --volumes 3 | 4 | build: 5 | PYTHON_VERSION=${PYTHON_VERSION} docker-compose build client 6 | 7 | pull: 8 | OPENSEARCH_VERSION=${OPENSEARCH_VERSION} PYTHON_VERSION=${PYTHON_VERSION} docker-compose pull 9 | 10 | push: 11 | # requires authentication. 12 | PYTHON_VERSION=${PYTHON_VERSION} docker-compose push client 13 | 14 | run_tests: 15 | OPENSEARCH_VERSION=${OPENSEARCH_VERSION} PYTHON_VERSION=${PYTHON_VERSION} docker-compose -p "${OPEN_VERSION}-${PYTHON_VERSION}" run client python setup.py test 16 | 17 | start_opensearch: 18 | OPENSEARCH_VERSION=${OPENSEARCH_VERSION} docker-compose up -d opensearch 19 | -------------------------------------------------------------------------------- /ADMINS.md: -------------------------------------------------------------------------------- 1 | - [Current Admins](#current-admins) 2 | - [Admin Responsibilities](#admin-responsibilities) 3 | 4 | ## Current Admins 5 | 6 | | Admin | GitHub ID | Affiliation | 7 | | ---------------- | --------------------------------------- | ----------- | 8 | | Charlotte Henkle | [CEHENKLE](https://github.com/CEHENKLE) | Amazon | 9 | | Henri Yandell | [hyandell](https://github.com/hyandell) | Amazon | 10 | 11 | 12 | ## Admin Responsibilities 13 | 14 | [This document](https://github.com/opensearch-project/.github/blob/main/ADMINS.md#admin-responsibilities) explains what admins do in this repo, and how they should be doing it. -------------------------------------------------------------------------------- /utils/templates/overrides/indices/put_mapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "url": { 3 | "paths": [ 4 | { 5 | "path": "/{index}/_mapping", 6 | "methods": [ 7 | "POST", 8 | "PUT" 9 | ], 10 | "parts": { 11 | "index": { 12 | "type": "string", 13 | "description": "Comma-separated list of indices; use `_all` or empty string to perform the operation on all indices.", 14 | "required": false 15 | } 16 | } 17 | } 18 | ] 19 | } 20 | } -------------------------------------------------------------------------------- /docs/source/api-ref/client.md: -------------------------------------------------------------------------------- 1 | # Clients 2 | 3 | ```{toctree} 4 | --- 5 | glob: 6 | titlesonly: 7 | maxdepth: 1 8 | --- 9 | 10 | clients/opensearch_client 11 | ``` 12 | 13 | ## Clients within Opensearch 14 | 15 | OpenSearch Client includes several clients: 16 | 17 | ```{toctree} 18 | --- 19 | glob: 20 | titlesonly: 21 | maxdepth: 1 22 | --- 23 | 24 | clients/http_client 25 | clients/cat_client 26 | clients/cluster_client 27 | clients/dangling_indices_client 28 | clients/ingest_client 29 | clients/indices_client 30 | clients/nodes_client 31 | clients/remote_client 32 | clients/security_client 33 | clients/snapshot_client 34 | clients/tasks_client 35 | clients/features_client 36 | ``` 37 | -------------------------------------------------------------------------------- /samples/aws/README.md: -------------------------------------------------------------------------------- 1 | ## AWS SigV4 Samples 2 | 3 | Create an OpenSearch domain in (AWS) which support IAM based AuthN/AuthZ. 4 | 5 | ``` 6 | export AWS_ACCESS_KEY_ID= 7 | export AWS_SECRET_ACCESS_KEY= 8 | export AWS_SESSION_TOKEN= 9 | export AWS_REGION=us-west-2 10 | 11 | export SERVICE=es # use "aoss" for OpenSearch Serverless. 12 | export ENDPOINT=https://....us-west-2.es.amazonaws.com 13 | 14 | poetry run python aws/search_urllib_sync.py 15 | poetry run python aws/search_urllib_async.py 16 | ``` 17 | 18 | This will output the version of OpenSearch and a search result. 19 | 20 | ``` 21 | opensearch: 2.3.0 22 | {'director': 'Bennett Miller', 'title': 'Moneyball', 'year': 2011} 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/source/index.md: -------------------------------------------------------------------------------- 1 | # OpenSearch Python Client Documentation 2 | 3 | ## Table of Contents 4 | 5 | ```{toctree} 6 | --- 7 | maxdepth: 1 8 | --- 9 | 10 | api-ref 11 | genindex 12 | License 13 | Contributing 14 | Code of Conduct 15 | Developer Guide 16 | GitHub Repository 17 | ``` 18 | 19 | ```{include} ../../README.md 20 | 21 | ``` 22 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Build & Deploy Docs 2 | on: [push, pull_request, workflow_dispatch] 3 | 4 | jobs: 5 | build-and-deploy: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v6 9 | - uses: actions/setup-python@v6 10 | with: 11 | python-version: '3.10' 12 | 13 | - name: Install Dependencies 14 | run: | 15 | python3.10 -m pip install nox 16 | 17 | - name: Make 18 | run: | 19 | nox -rs docs 20 | 21 | - name: Deploy 22 | uses: peaceiris/actions-gh-pages@v4 23 | if: github.ref == 'refs/heads/main' 24 | with: 25 | github_token: ${{ secrets.GITHUB_TOKEN }} 26 | publish_dir: ./docs/build/html 27 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= -W 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | # OpenSearch Python Samples 2 | 3 | Most samples can be run using OpenSearch installed locally with Docker. 4 | 5 | ## Admin User Password 6 | 7 | Add the default `admin` password to the environment. 8 | 9 | ``` 10 | export OPENSEARCH_PASSWORD=myStrongPassword123! 11 | ``` 12 | 13 | ## Start the Container 14 | 15 | ``` 16 | docker pull opensearchproject/opensearch:latest 17 | docker run -d -p 9200:9200 -p 9600:9600 -e OPENSEARCH_INITIAL_ADMIN_PASSWORD=$OPENSEARCH_PASSWORD -e "discovery.type=single-node" opensearchproject/opensearch:latest 18 | ``` 19 | 20 | ## Install Python Prerequisites 21 | 22 | Install [poetry](https://python-poetry.org/docs/). 23 | 24 | ## Run Samples 25 | 26 | ``` 27 | poetry install 28 | poetry run python hello/hello.py 29 | ``` 30 | -------------------------------------------------------------------------------- /.github/workflows/backport.yml: -------------------------------------------------------------------------------- 1 | name: Backport 2 | on: 3 | pull_request_target: 4 | types: 5 | - closed 6 | - labeled 7 | 8 | jobs: 9 | backport: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: write 13 | pull-requests: write 14 | name: Backport 15 | steps: 16 | - name: GitHub App token 17 | id: github_app_token 18 | uses: tibdex/github-app-token@v2.1.0 19 | with: 20 | app_id: ${{ secrets.APP_ID }} 21 | private_key: ${{ secrets.APP_PRIVATE_KEY }} 22 | installation_id: 22958780 23 | 24 | - name: Backport 25 | uses: VachaShah/backport@v2.2.0 26 | with: 27 | github_token: ${{ steps.github_app_token.outputs.token }} 28 | label_pattern: backport/backport-${{ github.event.number }} 29 | -------------------------------------------------------------------------------- /guides/proxy.md: -------------------------------------------------------------------------------- 1 | - [Using a Proxy](#using-a-proxy) 2 | - [Using a Proxy with a Sync Client](#using-a-proxy-with-a-sync-client) 3 | - [Using a Proxy with an Async Client](#using-a-proxy-with-an-async-client) 4 | 5 | # Using a Proxy 6 | 7 | ## Using a Proxy with a Sync Client 8 | 9 | ```python 10 | from opensearchpy import OpenSearch, RequestsHttpConnection 11 | 12 | OpenSearch( 13 | hosts=["https://..."], 14 | use_ssl=True, 15 | verify_certs=True, 16 | connection_class=RequestsHttpConnection, 17 | trust_env=True, 18 | ) 19 | ``` 20 | 21 | ## Using a Proxy with an Async Client 22 | 23 | ```python 24 | from opensearchpy import AsyncOpenSearch, AIOHttpConnection 25 | 26 | client = AsyncOpenSearch( 27 | hosts=["https://..."], 28 | use_ssl=True, 29 | verify_certs=True, 30 | connection_class=AIOHttpConnection, 31 | trust_env=True, 32 | ) 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.https://www.sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /guides/point_in_time.md: -------------------------------------------------------------------------------- 1 | - [Point-in-Time](#point-in-time) 2 | 3 | ### Point-in-Time 4 | 5 | [Point in Time (PIT)](https://opensearch.org/docs/latest/search-plugins/point-in-time/) lets you run different queries against a dataset that is fixed in time. 6 | 7 | Create a point in time on an index. 8 | 9 | ```python 10 | index_name = "test-index" 11 | response = client.create_pit( 12 | index=index_name, 13 | params={"keep_alive": "1m"} 14 | ) 15 | 16 | pit_id = response.get("pit_id") 17 | print('\n Point in time ID:') 18 | print(pit_id) 19 | ``` 20 | 21 | 22 | List all point in time which are alive in the cluster. 23 | 24 | ```python 25 | response = client.get_all_pits() 26 | print(response) 27 | ``` 28 | 29 | Delete a point in time. 30 | 31 | ```python 32 | pit_body = {"pit_id": pit_id} 33 | response = client.delete_pit(body=pit_body) 34 | print(response) 35 | ``` 36 | 37 | Delete all point in time. 38 | 39 | ```python 40 | response = client.delete_all_pits() 41 | print(response) 42 | ``` 43 | -------------------------------------------------------------------------------- /utils/templates/overrides/indices/put_alias-func_params: -------------------------------------------------------------------------------- 1 | {# Work around for https://github.com/opensearch-project/opensearch-py/issues/803 #} 2 | 3 | {% set first_params = ["index", "name"] %} 4 | 5 | {% for p, info in api.all_parts.items() %} 6 | {% if info.required %}{{ p }}: {{ info.type }}, {% endif %} 7 | {% endfor %} 8 | 9 | {% for p, info in api.all_parts.items() if p in first_params %} 10 | {% if not info.type == 'Any' %}{{ p }}: Optional[{{ info.type }}]=None, {% endif %} 11 | {% if info.type == 'Any' %}{{ p }}: {{ info.type }}=None, {% endif %} 12 | {% endfor %} 13 | 14 | {% if api.body %} 15 | body{% if not api.body.required %}: Any=None{% else %}: Any{% endif %}, 16 | {% endif %} 17 | 18 | {% for p, info in api.all_parts.items() if p not in first_params %} 19 | {% if not info.required and not info.type == 'Any' %}{{ p }}: Optional[{{ info.type }}]=None, {% endif %} 20 | {% if not info.required and info.type == 'Any' %}{{ p }}: {{ info.type }}=None, {% endif %} 21 | {% endfor %} 22 | 23 | params: Any=None, 24 | headers: Any=None, 25 | -------------------------------------------------------------------------------- /guides/dsl.md: -------------------------------------------------------------------------------- 1 | - [High Level DSL](#high-level-dsl) 2 | 3 | ## High Level DSL 4 | 5 | The opensearch-py client includes a high level interface called opensearch-py-dsl that supports creating and indexing documents, searching with and without filters, and updating documents using queries. See [opensearch-dsl-py client documentation](https://opensearch.org/docs/latest/clients/python-high-level/) for details and [the API reference](https://github.com/opensearch-project/opensearch-py/tree/main/docs/source/api-ref). 6 | 7 | In the below example, [Search API](https://github.com/opensearch-project/opensearch-py/blob/main/opensearchpy/helpers/search.py) from opensearch-dsl-py client is used. 8 | 9 | ```python 10 | from opensearchpy import OpenSearch, Search 11 | 12 | client = OpenSearch(...) 13 | 14 | s = Search( 15 | using=client, 16 | index=index_name 17 | ) 18 | .filter("term", category="search") 19 | .query("match", title="python") 20 | 21 | response = s.execute() 22 | 23 | for hit in response: 24 | print(hit.meta.score, hit.title) 25 | ``` -------------------------------------------------------------------------------- /test_opensearchpy/test_client/test_plugins/test_plugins_client.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | import re 11 | 12 | from opensearchpy.client import OpenSearch 13 | 14 | from ...test_cases import TestCase 15 | 16 | 17 | class TestPluginsClient(TestCase): 18 | def test_plugins_client(self) -> None: 19 | with self.assertWarns(Warning) as w: 20 | client = OpenSearch() 21 | # double-init 22 | client.plugins.__init__(client) # type: ignore # pylint: disable=unnecessary-dunder-call 23 | self.assertTrue( 24 | re.match( 25 | r"Cannot load `\w+` directly to OpenSearch as it already exists. Use `OpenSearch.plugin.\w+` instead.", 26 | str(w.warnings[0].message), 27 | ) 28 | ) 29 | -------------------------------------------------------------------------------- /COMPATIBILITY.md: -------------------------------------------------------------------------------- 1 | - [Compatibility with OpenSearch](#compatibility-with-opensearch) 2 | - [Upgrading](#upgrading) 3 | 4 | ## Compatibility with OpenSearch 5 | 6 | The below matrix shows the compatibility of the [`opensearch-py`](https://pypi.org/project/opensearch-py/) with versions of [`OpenSearch`](https://opensearch.org/downloads.html#opensearch). 7 | 8 | | Client Version | OpenSearch Version | Notes | 9 | | --- | --- | --- | 10 | | 1.0.0 | 1.0.0-1.2.4 | | 11 | | 1.1.0 | 1.3.0-1.3.7 | | 12 | | 2.x.x | 1.0.0-2.x | client works against OpenSearch 1.x as long as features removed in 2.0 are not used | 13 | | 3.x.x | 1.0.0-3.x | client works against OpenSearch 1.x, 2.x as long as features removed in 3.0 are not used | 14 | 15 | ## Upgrading 16 | 17 | Major versions of OpenSearch introduce breaking changes that require careful upgrades of the client. While `opensearch-py-client` 2.0.0 works against the latest OpenSearch 1.x, certain deprecated features removed in OpenSearch 2.0 have also been removed from the client. Please refer to the [OpenSearch documentation](https://opensearch.org/docs/latest/clients/index/) for more information. 18 | -------------------------------------------------------------------------------- /test_opensearchpy/test_server_secured/test_clients.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | import os 11 | from unittest import TestCase 12 | 13 | from opensearchpy import OpenSearch 14 | from opensearchpy.helpers.test import OPENSEARCH_URL 15 | 16 | 17 | class TestSecurity(TestCase): 18 | def test_security(self) -> None: 19 | password = os.environ.get("OPENSEARCH_INITIAL_ADMIN_PASSWORD", "admin") 20 | client = OpenSearch( 21 | OPENSEARCH_URL, 22 | http_auth=("admin", password), 23 | verify_certs=False, 24 | ) 25 | 26 | info = client.info() 27 | self.assertNotEqual(info["version"]["number"], "") 28 | self.assertNotEqual(info["tagline"], "") 29 | self.assertTrue( 30 | "build_flavor" in info["version"] or "distribution" in info["version"] 31 | ) 32 | -------------------------------------------------------------------------------- /test_opensearchpy/test_async/test_plugins_client.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | 11 | import re 12 | import warnings 13 | 14 | import pytest 15 | from _pytest.mark.structures import MarkDecorator 16 | 17 | from opensearchpy._async.client import AsyncOpenSearch 18 | 19 | pytestmark: MarkDecorator = pytest.mark.asyncio 20 | 21 | 22 | class TestPluginsClient: 23 | async def test_plugins_client(self) -> None: 24 | with warnings.catch_warnings(record=True) as w: 25 | client = AsyncOpenSearch() 26 | # testing double-init here 27 | client.plugins.__init__(client) # type: ignore # pylint: disable=unnecessary-dunder-call 28 | assert re.match( 29 | r"Cannot load `\w+` directly to AsyncOpenSearch as it already exists. Use `AsyncOpenSearch.plugin.\w+` instead.", 30 | str(w[0].message), 31 | ) 32 | -------------------------------------------------------------------------------- /benchmarks/thread_with_return_value.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | 11 | from threading import Thread 12 | from typing import Any, Optional 13 | 14 | 15 | class ThreadWithReturnValue(Thread): 16 | _target: Any 17 | _args: Any 18 | _kwargs: Any 19 | 20 | def __init__( 21 | self, 22 | group: Any = None, 23 | target: Any = None, 24 | name: Optional[str] = None, 25 | args: Any = (), 26 | kwargs: Any = {}, 27 | Verbose: Optional[bool] = None, 28 | ) -> None: 29 | Thread.__init__(self, group, target, name, args, kwargs) 30 | self._return = None 31 | 32 | def run(self) -> None: 33 | if self._target is not None: 34 | self._return = self._target(*self._args, **self._kwargs) 35 | 36 | def join(self, *args: Any) -> Any: 37 | Thread.join(self, *args) 38 | return self._return 39 | -------------------------------------------------------------------------------- /test_opensearchpy/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | -------------------------------------------------------------------------------- /opensearchpy/_async/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | -------------------------------------------------------------------------------- /opensearchpy/metrics/metrics.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | from abc import ABC, abstractmethod 11 | from typing import Optional 12 | 13 | 14 | class Metrics(ABC): 15 | """ 16 | The Metrics class defines methods and properties for managing 17 | request metrics, including start time, end time, and service time, 18 | serving as a blueprint for concrete implementations. 19 | """ 20 | 21 | @abstractmethod 22 | def request_start(self) -> None: 23 | pass 24 | 25 | @abstractmethod 26 | def request_end(self) -> None: 27 | pass 28 | 29 | @property 30 | @abstractmethod 31 | def start_time(self) -> Optional[float]: 32 | pass 33 | 34 | @property 35 | @abstractmethod 36 | def end_time(self) -> Optional[float]: 37 | pass 38 | 39 | @property 40 | @abstractmethod 41 | def service_time(self) -> Optional[float]: 42 | pass 43 | -------------------------------------------------------------------------------- /test_opensearchpy/test_async/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | -------------------------------------------------------------------------------- /test_opensearchpy/test_client/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | -------------------------------------------------------------------------------- /test_opensearchpy/test_helpers/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | -------------------------------------------------------------------------------- /test_opensearchpy/test_connection/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | -------------------------------------------------------------------------------- /test_opensearchpy/test_async/test_helpers/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | -------------------------------------------------------------------------------- /test_opensearchpy/test_client/test_plugins/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | -------------------------------------------------------------------------------- /test_opensearchpy/test_server/test_helpers/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | -------------------------------------------------------------------------------- /test_opensearchpy/test_server/test_plugins/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | -------------------------------------------------------------------------------- /test_opensearchpy/test_client/test_point_in_time.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | from test_opensearchpy.test_cases import OpenSearchTestCase 11 | 12 | 13 | class TestPointInTime(OpenSearchTestCase): 14 | def test_create_pit(self) -> None: 15 | index_name = "test-index" 16 | self.client.create_pit(index=index_name) 17 | self.assert_url_called("POST", "/test-index/_search/point_in_time") 18 | 19 | def test_delete_pit(self) -> None: 20 | self.client.delete_pit(body={"pit_id": ["Sample-PIT-ID"]}) 21 | self.assert_url_called("DELETE", "/_search/point_in_time") 22 | 23 | def test_delete_all_pits(self) -> None: 24 | self.client.delete_all_pits() 25 | self.assert_url_called("DELETE", "/_search/point_in_time/_all") 26 | 27 | def test_get_all_pits(self) -> None: 28 | self.client.get_all_pits() 29 | self.assert_url_called("GET", "/_search/point_in_time/_all") 30 | -------------------------------------------------------------------------------- /opensearchpy/_version.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | __versionstr__: str = "3.1.0" 28 | -------------------------------------------------------------------------------- /test_opensearchpy/test_async/test_server/test_helpers/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | -------------------------------------------------------------------------------- /test_opensearchpy/test_async/test_server/test_plugins/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | -------------------------------------------------------------------------------- /opensearchpy/_async/compat.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | from asyncio import get_running_loop 29 | 30 | from ..compat import * # noqa 31 | 32 | __all__ = ["get_running_loop"] 33 | -------------------------------------------------------------------------------- /test_opensearchpy/test_client/test_requests.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | from unittest import TestCase 11 | 12 | from opensearchpy import OpenSearch, RequestsHttpConnection 13 | 14 | 15 | class TestRequests(TestCase): 16 | def test_connection_class(self) -> None: 17 | client = OpenSearch(connection_class=RequestsHttpConnection) 18 | self.assertEqual(client.transport.pool_maxsize, None) 19 | self.assertEqual(client.transport.connection_class, RequestsHttpConnection) 20 | self.assertIsInstance( 21 | client.transport.connection_pool.connections[0], RequestsHttpConnection 22 | ) 23 | 24 | def test_pool_maxsize(self) -> None: 25 | client = OpenSearch(connection_class=RequestsHttpConnection, pool_maxsize=42) 26 | self.assertEqual(client.transport.pool_maxsize, 42) 27 | self.assertEqual( 28 | client.transport.connection_pool.connections[0] 29 | .session.adapters["https://"] 30 | ._pool_maxsize, 31 | 42, 32 | ) 33 | -------------------------------------------------------------------------------- /.github/workflows/integration.yml: -------------------------------------------------------------------------------- 1 | name: Integration Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | integration-pre-212: 7 | name: Integ-pre-212 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | opensearch_version: [ '1.0.1', '1.3.20' ] 13 | secured: [ "true", "false" ] 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v6 17 | - name: Integ OpenSearch secured=${{ matrix.secured }} version=${{ matrix.opensearch_version }} 18 | run: "./.ci/run-tests ${{ matrix.secured }} ${{ matrix.opensearch_version }}" 19 | 20 | integration-post-212: 21 | name: Integ-post-212 22 | runs-on: ubuntu-latest 23 | env: 24 | OPENSEARCH_URL: 'https://admin:myStrongPassword123!@localhost:9200' 25 | OPENSEARCH_INITIAL_ADMIN_PASSWORD: 'myStrongPassword123!' 26 | strategy: 27 | fail-fast: false 28 | matrix: 29 | opensearch_version: [ '2.18.0', '2.19.4', '3.3.2' ] 30 | secured: [ "true", "false" ] 31 | 32 | steps: 33 | - name: Checkout 34 | uses: actions/checkout@v6 35 | - name: Integ OpenSearch secured=${{ matrix.secured }} version=${{ matrix.opensearch_version }} 36 | run: "./.ci/run-tests ${{ matrix.secured }} ${{ matrix.opensearch_version }}" 37 | -------------------------------------------------------------------------------- /.github/workflows/dependabot_pr.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot PR actions 2 | on: 3 | pull_request: 4 | types: 5 | - opened 6 | - reopened 7 | - synchronize 8 | - labeled 9 | - unlabeled 10 | 11 | jobs: 12 | dependabot: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | pull-requests: write 16 | contents: write 17 | if: ${{ github.actor == 'dependabot[bot]' }} 18 | steps: 19 | - name: GitHub App token 20 | id: github_app_token 21 | uses: tibdex/github-app-token@v2.1.0 22 | with: 23 | app_id: ${{ secrets.APP_ID }} 24 | private_key: ${{ secrets.APP_PRIVATE_KEY }} 25 | installation_id: 22958780 26 | 27 | - name: Check out code 28 | uses: actions/checkout@v6 29 | with: 30 | token: ${{ steps.github_app_token.outputs.token }} 31 | 32 | - name: Update the changelog 33 | uses: dangoslen/dependabot-changelog-helper@v4 34 | with: 35 | version: 'Unreleased' 36 | 37 | - name: Commit the changes 38 | uses: stefanzweifel/git-auto-commit-action@v7 39 | with: 40 | commit_message: "Update changelog" 41 | branch: ${{ github.head_ref }} 42 | commit_user_name: dependabot[bot] 43 | commit_user_email: support@github.com 44 | commit_options: '--signoff' -------------------------------------------------------------------------------- /test_opensearchpy/test_client/test_search_pipeline.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | from test_opensearchpy.test_cases import OpenSearchTestCase 11 | 12 | 13 | class TestSearchPipeline(OpenSearchTestCase): 14 | def test_create_search_pipeline(self) -> None: 15 | body = { 16 | "request_processors": [ 17 | { 18 | "filter_query": { 19 | "tag": "tag1", 20 | "description": "This processor returns only publicly visible documents", 21 | "query": {"term": {"visibility": "public"}}, 22 | } 23 | } 24 | ], 25 | "response_processors": [ 26 | {"rename_field": {"field": "message", "target_field": "notification"}} 27 | ], 28 | } 29 | 30 | self.client.search_pipeline.put(id="my_pipeline", body=body) 31 | self.assert_url_called("PUT", "/_search/pipeline/my_pipeline") 32 | 33 | def test_get_search_pipeline(self) -> None: 34 | self.client.search_pipeline.get(id="my_pipeline") 35 | self.assert_url_called("GET", "/_search/pipeline/my_pipeline") 36 | -------------------------------------------------------------------------------- /samples/security/users.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # The OpenSearch Contributors require contributions made to 6 | # this file be licensed under the Apache-2.0 license or a 7 | # compatible open source license. 8 | # 9 | # Modifications Copyright OpenSearch Contributors. See 10 | # GitHub history for details. 11 | 12 | import os 13 | 14 | from opensearchpy import OpenSearch 15 | 16 | 17 | def main() -> None: 18 | """ 19 | A basic OpenSearch sample that create and manage users. 20 | """ 21 | # connect to OpenSearch 22 | 23 | host = "localhost" 24 | port = 9200 25 | auth = ( 26 | "admin", 27 | os.getenv("OPENSEARCH_PASSWORD", "admin"), 28 | ) # For testing only. Don't store credentials in code. 29 | 30 | client = OpenSearch( 31 | hosts=[{"host": host, "port": port}], 32 | http_auth=auth, 33 | use_ssl=True, 34 | verify_certs=False, 35 | ssl_show_warn=False, 36 | ) 37 | 38 | # Create a User 39 | 40 | user_name = "test-user" 41 | user_content = {"password": "opensearch@123", "opendistro_security_roles": []} 42 | 43 | response = client.security.create_user(username=user_name, body=user_content) 44 | print(response) 45 | 46 | # Get a User 47 | 48 | user_name = "test-user" 49 | 50 | response = client.security.get_user(username=user_name) 51 | print(response) 52 | 53 | 54 | if __name__ == "__main__": 55 | main() 56 | -------------------------------------------------------------------------------- /.ci/functions/wait-for-container.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Exposes a routine scripts can call to wait for a container if that container set up a health command 4 | # 5 | # Please source .ci/functions/imports.sh as a whole not just this file 6 | # 7 | # Version 1.0.1 8 | # - Initial version after refactor 9 | # - Make sure wait_for_contiainer is silent 10 | 11 | function wait_for_container { 12 | set +x 13 | until ! container_running "$1" || (container_running "$1" && [[ "$(docker inspect -f "{{.State.Health.Status}}" ${1})" != "starting" ]]); do 14 | echo "" 15 | docker inspect -f "{{range .State.Health.Log}}{{.Output}}{{end}}" ${1} 16 | echo -e "\033[34;1mINFO:\033[0m waiting for node $1 to be up\033[0m" 17 | sleep 2; 18 | done; 19 | 20 | # Always show logs if the container is running, this is very useful both on CI as well as while developing 21 | if container_running $1; then 22 | docker logs $1 23 | fi 24 | 25 | if ! container_running $1 || [[ "$(docker inspect -f "{{.State.Health.Status}}" ${1})" != "healthy" ]]; then 26 | cleanup_all_in_network $2 27 | echo 28 | echo -e "\033[31;1mERROR:\033[0m Failed to start $1 in detached mode beyond health checks\033[0m" 29 | echo -e "\033[31;1mERROR:\033[0m dumped the docker log before shutting the node down\033[0m" 30 | return 1 31 | else 32 | echo 33 | echo -e "\033[32;1mSUCCESS:\033[0m Detached and healthy: ${1} on docker network: ${network_name}\033[0m" 34 | return 0 35 | fi 36 | } 37 | -------------------------------------------------------------------------------- /opensearchpy/_async/helpers/test.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | import asyncio 10 | import os 11 | from typing import Any 12 | from unittest import SkipTest 13 | 14 | from opensearchpy import AsyncOpenSearch 15 | from opensearchpy.exceptions import ConnectionError 16 | 17 | OPENSEARCH_URL = os.environ.get("OPENSEARCH_URL", "https://localhost:9200") 18 | 19 | 20 | async def get_test_client(nowait: bool = False, **kwargs: Any) -> Any: 21 | # construct kwargs from the environment 22 | kw = {"timeout": 30} 23 | 24 | from opensearchpy import AsyncConnection 25 | 26 | async_connection = AsyncConnection() 27 | if hasattr(async_connection, "AIOHttpConnection"): 28 | kw["connection_class"] = getattr(async_connection, "AIOHttpConnection") 29 | 30 | kw.update(kwargs) 31 | client = AsyncOpenSearch(OPENSEARCH_URL, **kw) # type: ignore 32 | 33 | # wait for yellow status 34 | for _ in range(1 if nowait else 100): 35 | try: 36 | await client.cluster.health(wait_for_status="yellow") 37 | return client 38 | except ConnectionError: 39 | await asyncio.sleep(0.1) 40 | else: 41 | # timeout 42 | raise SkipTest("OpenSearch failed to start.") 43 | -------------------------------------------------------------------------------- /.ci/run-tests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Entrypoint to run integration tests 4 | 5 | # Default environment variables 6 | export TEST_SUITE="${TEST_SUITE:=oss}" 7 | export PYTHON_VERSION="${PYTHON_VERSION:=3.10}" 8 | export PYTHON_CONNECTION_CLASS="${PYTHON_CONNECTION_CLASS:=Urllib3HttpConnection}" 9 | export CLUSTER="${CLUSTER:-opensearch}" 10 | export SECURE_INTEGRATION="${1:-false}" 11 | export OPENSEARCH_VERSION="${2:-latest}" 12 | export TEST_PATTERN="${3:-}" 13 | if [[ "$SECURE_INTEGRATION" == "true" ]]; then 14 | export OPENSEARCH_URL_EXTENSION="https" 15 | else 16 | export OPENSEARCH_URL_EXTENSION="http" 17 | fi 18 | 19 | export IS_UNRELEASED=false 20 | if [[ "$OPENSEARCH_VERSION" == *"SNAPSHOT" ]]; then 21 | IS_UNRELEASED=true 22 | fi 23 | 24 | echo -e "\033[1m>>>>> Unreleased is $IS_UNRELEASED >>>>>>>>>>>>>>>>>>>>>>>>>>>>>\033[0m" 25 | script_path=$(dirname $(realpath $0)) 26 | source $script_path/functions/imports.sh 27 | set -euo pipefail 28 | 29 | echo -e "\033[1m>>>>> Start server container >>>>>>>>>>>>>>>>>>>>>>>>>>>>>\033[0m" 30 | DETACH=true bash .ci/run-opensearch.sh 31 | 32 | if [[ -n "$RUNSCRIPTS" ]]; then 33 | for RUNSCRIPT in ${RUNSCRIPTS//,/ } ; do 34 | echo -e "\033[1m>>>>> Running run-$RUNSCRIPT.sh >>>>>>>>>>>>>>>>>>>>>>>>>>>>>\033[0m" 35 | CONTAINER_NAME=${RUNSCRIPT} \ 36 | DETACH=true \ 37 | bash .ci/run-${RUNSCRIPT}.sh 38 | done 39 | fi 40 | 41 | echo -e "\033[1m>>>>> Repository specific tests >>>>>>>>>>>>>>>>>>>>>>>>>>>>>\033[0m" 42 | bash .ci/run-repository.sh 43 | -------------------------------------------------------------------------------- /opensearchpy/metrics/metrics_none.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | from typing import Optional 11 | 12 | from opensearchpy.metrics.metrics import Metrics 13 | 14 | 15 | class MetricsNone(Metrics): 16 | """ 17 | Default metrics class. It sets the start time, end time, and service time to None. 18 | """ 19 | 20 | @property 21 | def start_time(self) -> Optional[float]: 22 | return self._start_time 23 | 24 | @property 25 | def end_time(self) -> Optional[float]: 26 | return self._end_time 27 | 28 | @property 29 | def service_time(self) -> Optional[float]: 30 | return self._service_time 31 | 32 | def __init__(self) -> None: 33 | self._start_time: Optional[float] = None 34 | self._end_time: Optional[float] = None 35 | self._service_time: Optional[float] = None 36 | 37 | # request_start and request_end are placeholders, 38 | # not implementing actual metrics collection in this subclass. 39 | 40 | def request_start(self) -> None: 41 | self._start_time = None 42 | self._end_time = None 43 | self._service_time = None 44 | 45 | def request_end(self) -> None: 46 | self._end_time = None 47 | self._service_time = None 48 | -------------------------------------------------------------------------------- /docs/source/api-ref/exceptions.md: -------------------------------------------------------------------------------- 1 | # exceptions 2 | 3 | ```{eval-rst} 4 | .. autoclass:: opensearchpy.AuthenticationException 5 | ``` 6 | 7 | ```{eval-rst} 8 | .. autoclass:: opensearchpy.AuthorizationException 9 | ``` 10 | 11 | ```{eval-rst} 12 | .. autoclass:: opensearchpy.ConflictError 13 | ``` 14 | 15 | ```{eval-rst} 16 | .. autoclass:: opensearchpy.ConnectionError 17 | ``` 18 | 19 | ```{eval-rst} 20 | .. autoclass:: opensearchpy.ConnectionTimeout 21 | ``` 22 | 23 | ```{eval-rst} 24 | .. autoclass:: opensearchpy.ImproperlyConfigured 25 | ``` 26 | 27 | ```{eval-rst} 28 | .. autoclass:: opensearchpy.NotFoundError 29 | ``` 30 | 31 | ```{eval-rst} 32 | .. autoclass:: opensearchpy.OpenSearchDeprecationWarning 33 | ``` 34 | 35 | ```{eval-rst} 36 | .. autoclass:: opensearchpy.OpenSearchException 37 | ``` 38 | 39 | ```{eval-rst} 40 | .. autoclass:: opensearchpy.OpenSearchWarning 41 | ``` 42 | 43 | ```{eval-rst} 44 | .. autoclass:: opensearchpy.RequestError 45 | ``` 46 | 47 | ```{eval-rst} 48 | .. autoclass:: opensearchpy.SerializationError 49 | ``` 50 | 51 | ```{eval-rst} 52 | .. autoclass:: opensearchpy.SSLError 53 | ``` 54 | 55 | ```{eval-rst} 56 | .. autoclass:: opensearchpy.TransportError 57 | ``` 58 | 59 | ```{eval-rst} 60 | .. autoclass:: opensearchpy.OpenSearchDslException 61 | ``` 62 | 63 | ```{eval-rst} 64 | .. autoclass:: opensearchpy.IllegalOperation 65 | ``` 66 | 67 | ```{eval-rst} 68 | .. autoclass:: opensearchpy.UnknownDslObject 69 | ``` 70 | 71 | ```{eval-rst} 72 | .. autoclass:: opensearchpy.ValidationException 73 | ``` 74 | -------------------------------------------------------------------------------- /guides/plugins/security.md: -------------------------------------------------------------------------------- 1 | - [Security Plugin](#security-plugin) 2 | - [Create a Role](#create-a-role) 3 | - [Get a Role](#get-a-role) 4 | - [Create a User](#create-a-user) 5 | - [Get a User](#get-a-user) 6 | 7 | ### Security Plugin 8 | 9 | The [Security Plugin API](https://opensearch.org/docs/latest/security/access-control/api/) lets you programmatically create and manage users, roles, role mappings, action groups, and tenants. 10 | 11 | #### Create a Role 12 | 13 | ```python 14 | role_name = "test-role" 15 | 16 | role_content = { 17 | "cluster_permissions": ["cluster_monitor"], 18 | "index_permissions": [ 19 | { 20 | "index_patterns": ["index", "test-*"], 21 | "allowed_actions": [ 22 | "data_access", 23 | "indices_monitor", 24 | ], 25 | } 26 | ], 27 | } 28 | 29 | response = client.security.create_role(role_name, body=role_content) 30 | print(response) 31 | ``` 32 | 33 | #### Get a Role 34 | 35 | ```python 36 | role_name = "test-role" 37 | 38 | response = client.security.get_role(role_name) 39 | print(response) 40 | ``` 41 | 42 | #### Create a User 43 | 44 | ```python 45 | user_name = "test-user" 46 | user_content = {"password": "test_password", "opendistro_security_roles": []} 47 | 48 | response = client.security.create_user(username=user_name, body=user_content) 49 | print(response) 50 | ``` 51 | 52 | #### Get a User 53 | 54 | ```python 55 | user_name = "test-user" 56 | 57 | response = client.security.get_user(user_name) 58 | print(response) 59 | ``` 60 | -------------------------------------------------------------------------------- /.ci/functions/imports.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Sets up all the common variables and imports relevant functions 4 | # 5 | # Version 1.0.1 6 | # - Initial version after refactor 7 | 8 | if [[ -z $opensearch_node_name ]]; then 9 | # only set these once 10 | set -euo pipefail 11 | export TEST_SUITE=${TEST_SUITE-oss} 12 | export RUNSCRIPTS=${RUNSCRIPTS-} 13 | export DETACH=${DETACH-false} 14 | export CLEANUP=${CLEANUP-false} 15 | export OPENSEARCH_URL_EXTENSION=${OPENSEARCH_URL_EXTENSION-http} 16 | 17 | export opensearch_node_name=instance 18 | export opensearch_image=opensearchproject/opensearch 19 | 20 | export opensearch_url=$OPENSEARCH_URL_EXTENSION://${opensearch_node_name}:9200 21 | export external_opensearch_url=${opensearch_url/$opensearch_node_name/localhost} 22 | 23 | export network_name=search-rest-test 24 | 25 | export ssl_cert="${script_path}/certs/testnode.crt" 26 | export ssl_key="${script_path}/certs/testnode.key" 27 | export ssl_ca="${script_path}/certs/ca.crt" 28 | 29 | fi 30 | 31 | export script_path=$(dirname $(realpath $0)) 32 | source $script_path/functions/cleanup.sh 33 | source $script_path/functions/wait-for-container.sh 34 | trap "cleanup_trap ${network_name}" EXIT 35 | 36 | 37 | if [[ "$CLEANUP" == "true" ]]; then 38 | cleanup_all_in_network $network_name 39 | exit 0 40 | fi 41 | 42 | echo -e "\033[34;1mINFO:\033[0m Creating network $network_name if it does not exist already \033[0m" 43 | docker network inspect "$network_name" > /dev/null 2>&1 || docker network create "$network_name" 44 | -------------------------------------------------------------------------------- /opensearchpy/client/remote.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | from typing import Any 29 | 30 | from .utils import NamespacedClient, query_params 31 | 32 | 33 | class RemoteClient(NamespacedClient): 34 | @query_params() 35 | def info(self, params: Any = None, headers: Any = None) -> Any: 36 | return self.transport.perform_request( 37 | "GET", "/_remote/info", params=params, headers=headers 38 | ) 39 | -------------------------------------------------------------------------------- /opensearchpy/_async/client/remote.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | from typing import Any 29 | 30 | from .utils import NamespacedClient, query_params 31 | 32 | 33 | class RemoteClient(NamespacedClient): 34 | @query_params() 35 | async def info(self, params: Any = None, headers: Any = None) -> Any: 36 | return await self.transport.perform_request( 37 | "GET", "/_remote/info", params=params, headers=headers 38 | ) 39 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | strategy: 8 | matrix: 9 | entry: 10 | - { os: 'ubuntu-latest', python-version: "3.10" } 11 | - { os: 'ubuntu-latest', python-version: "3.11" } 12 | - { os: 'macos-latest', python-version: "3.11" } 13 | - { os: 'windows-latest', python-version: "3.11" } 14 | - { os: 'ubuntu-latest', python-version: "3.12" } 15 | - { os: 'macos-latest', python-version: "3.12" } 16 | - { os: 'windows-latest', python-version: "3.12" } 17 | 18 | name: test (os=${{ matrix.entry.os }}, python=${{ matrix.entry.python-version }}) 19 | continue-on-error: ${{ matrix.entry.experimental || false }} 20 | runs-on: ${{ matrix.entry.os }} 21 | steps: 22 | - name: Checkout Source Code 23 | uses: actions/checkout@v6 24 | - name: Set Up Python - ${{ matrix.entry.python-version }} 25 | uses: actions/setup-python@v6 26 | with: 27 | python-version: ${{ matrix.entry.python-version }} 28 | env: 29 | PIP_NO_PYTHON_VERSION_WARNING: 1 30 | PIP_DISABLE_PIP_VERSION_CHECK: 1 31 | - name: Install Dependencies 32 | run: | 33 | python -m pip install nox 34 | - name: Run Tests 35 | run: | 36 | python -m nox -rs test-${{ matrix.entry.python-version }} 37 | - name: Upload coverage to Codecov 38 | uses: codecov/codecov-action@v5 39 | with: 40 | token: ${{ secrets.CODECOV_TOKEN }} 41 | files: ./junit/opensearch-py-codecov.xml 42 | -------------------------------------------------------------------------------- /opensearchpy/_async/client/utils.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | from ...client.utils import NamespacedClient # noqa 29 | from ...client.utils import ( 30 | SKIP_IN_PATH, 31 | _bulk_body, 32 | _escape, 33 | _make_path, 34 | _normalize_hosts, 35 | query_params, 36 | ) 37 | 38 | __all__ = [ 39 | "SKIP_IN_PATH", 40 | "NamespacedClient", 41 | "_make_path", 42 | "query_params", 43 | "_bulk_body", 44 | "_escape", 45 | "_normalize_hosts", 46 | ] 47 | -------------------------------------------------------------------------------- /test_opensearchpy/test_client/test_urllib3.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | from unittest import TestCase 11 | 12 | from urllib3.connectionpool import HTTPConnectionPool 13 | 14 | from opensearchpy import OpenSearch, Urllib3HttpConnection 15 | 16 | 17 | class TestUrlLib3(TestCase): 18 | def test_default(self) -> None: 19 | client = OpenSearch() 20 | self.assertEqual(client.transport.connection_class, Urllib3HttpConnection) 21 | self.assertEqual(client.transport.pool_maxsize, None) 22 | 23 | def test_connection_class(self) -> None: 24 | client = OpenSearch(connection_class=Urllib3HttpConnection) 25 | self.assertEqual(client.transport.connection_class, Urllib3HttpConnection) 26 | self.assertIsInstance( 27 | client.transport.connection_pool.connections[0], Urllib3HttpConnection 28 | ) 29 | self.assertIsInstance( 30 | client.transport.connection_pool.connections[0].pool, HTTPConnectionPool 31 | ) 32 | 33 | def test_pool_maxsize(self) -> None: 34 | client = OpenSearch(connection_class=Urllib3HttpConnection, pool_maxsize=42) 35 | self.assertEqual(client.transport.pool_maxsize, 42) 36 | # https://github.com/python/cpython/blob/3.12/Lib/queue.py#L35 37 | self.assertEqual( 38 | client.transport.connection_pool.connections[0].pool.pool.maxsize, 42 39 | ) 40 | -------------------------------------------------------------------------------- /UPGRADING.md: -------------------------------------------------------------------------------- 1 | - [Upgrading OpenSearch Python Client](#upgrading-opensearch-python-client) 2 | - [Upgrading to >= 2.2.0](#upgrading-to->=-2.2.0) 3 | - [Features from High-Level Python Client](#features-from-high-level-python-client) 4 | - [API Implementation differences for existing opensearch-dsl-py users](#api-implementation-differences-for-existing-opensearch-dsl-py-users) 5 | 6 | 7 | # Upgrading OpenSearch Python Client 8 | 9 | ## Upgrading to >= 2.2.0 10 | 11 | opensearch-py now includes [opensearch-dsl-py](https://pypi.org/project/opensearch-dsl/) features. opensearch-dsl-py was merged into opensearch-py preserving backwards compatibility with the previous opensearch-py version. (Refer [link](https://github.com/opensearch-project/opensearch-py/pull/287)) 12 | 13 | 14 | ### Features from High-Level Python Client 15 | 16 | opensearch-py functionalities that already exist are not altered. 17 | 18 | The opensearchpy [helpers](https://github.com/opensearch-project/opensearch-py/tree/main/opensearchpy/helpers) module now provides access to aggs, analysis, document, faceted search, field, function, index, mapping, query, search, update by query, utils, and wrappers. As a result, without importing opensearch-dsl-py, these functionalities can be imported directly from opensearch-py. 19 | 20 | 21 | ### API Implementation differences for existing opensearch-dsl-py users 22 | 23 | The functionalities from opensearch-dsl-py are merged into this client. Refer to the [USER_GUIDE](https://github.com/opensearch-project/opensearch-py/blob/main/USER_GUIDE.md#using-high-level-python-client) for an example on how to implement a feature from the previously known high level Python client. 24 | 25 | -------------------------------------------------------------------------------- /opensearchpy/connection/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | from .base import Connection 29 | from .http_requests import RequestsHttpConnection 30 | from .http_urllib3 import Urllib3HttpConnection, create_ssl_context 31 | 32 | __all__ = [ 33 | "Connection", 34 | "RequestsHttpConnection", 35 | "Urllib3HttpConnection", 36 | "create_ssl_context", 37 | ] 38 | 39 | try: 40 | from .http_async import AsyncHttpConnection 41 | 42 | __all__ += [ 43 | "AsyncHttpConnection", 44 | ] 45 | except (ImportError, SyntaxError): 46 | pass 47 | -------------------------------------------------------------------------------- /opensearchpy/helpers/asyncsigner.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | from typing import Any, Dict, Optional, Union 11 | 12 | from opensearchpy.helpers.signer import AWSV4Signer 13 | 14 | 15 | class AWSV4SignerAsyncAuth: 16 | """ 17 | AWS V4 Request Signer for Async Requests. 18 | """ 19 | 20 | def __init__(self, credentials: Any, region: str, service: str = "es") -> None: 21 | self.signer = AWSV4Signer(credentials, region, service) 22 | 23 | def __call__( 24 | self, 25 | method: str, 26 | url: str, 27 | body: Optional[Union[str, bytes]] = None, 28 | headers: Optional[Dict[str, str]] = None, 29 | ) -> Dict[str, str]: 30 | return self._sign_request(method=method, url=url, body=body, headers=headers) 31 | 32 | def _sign_request( 33 | self, 34 | method: str, 35 | url: str, 36 | body: Optional[Union[str, bytes]], 37 | headers: Optional[Dict[str, str]], 38 | ) -> Dict[str, str]: 39 | """ 40 | This method helps in signing the request by injecting the required headers. 41 | :param prepared_request: unsigned headers 42 | :return: signed headers 43 | """ 44 | 45 | updated_headers = self.signer.sign( 46 | method=method, 47 | url=url, 48 | body=body, 49 | headers=headers, 50 | ) 51 | return updated_headers 52 | -------------------------------------------------------------------------------- /opensearchpy/client/client.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | from typing import Any, Optional, Type 11 | 12 | from opensearchpy.client.utils import _normalize_hosts 13 | from opensearchpy.transport import Transport 14 | 15 | 16 | class Client: 17 | """ 18 | A generic async OpenSearch client. 19 | """ 20 | 21 | def __init__( 22 | self, 23 | hosts: Optional[str] = None, 24 | transport_class: Type[Transport] = Transport, 25 | **kwargs: Any 26 | ) -> None: 27 | """ 28 | :arg hosts: list of nodes, or a single node, we should connect to. 29 | Node should be a dictionary ({"host": "localhost", "port": 9200}), 30 | the entire dictionary will be passed to the :class:`~opensearchpy.Connection` 31 | class as kwargs, or a string in the format of ``host[:port]`` which will be 32 | translated to a dictionary automatically. If no value is given the 33 | :class:`~opensearchpy.Connection` class defaults will be used. 34 | 35 | :arg transport_class: :class:`~opensearchpy.Transport` subclass to use. 36 | 37 | :arg kwargs: any additional arguments will be passed on to the 38 | :class:`~opensearchpy.Transport` class and, subsequently, to the 39 | :class:`~opensearchpy.Connection` instances. 40 | """ 41 | self.transport = transport_class(_normalize_hosts(hosts), **kwargs) 42 | -------------------------------------------------------------------------------- /opensearchpy/_async/client/client.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | from typing import Any, Optional, Type 11 | 12 | from opensearchpy.client.utils import _normalize_hosts 13 | from opensearchpy.transport import Transport 14 | 15 | 16 | class Client: 17 | """ 18 | A generic async OpenSearch client. 19 | """ 20 | 21 | def __init__( 22 | self, 23 | hosts: Optional[str] = None, 24 | transport_class: Type[Transport] = Transport, 25 | **kwargs: Any 26 | ) -> None: 27 | """ 28 | :arg hosts: list of nodes, or a single node, we should connect to. 29 | Node should be a dictionary ({"host": "localhost", "port": 9200}), 30 | the entire dictionary will be passed to the :class:`~opensearchpy.Connection` 31 | class as kwargs, or a string in the format of ``host[:port]`` which will be 32 | translated to a dictionary automatically. If no value is given the 33 | :class:`~opensearchpy.Connection` class defaults will be used. 34 | 35 | :arg transport_class: :class:`~opensearchpy.Transport` subclass to use. 36 | 37 | :arg kwargs: any additional arguments will be passed on to the 38 | :class:`~opensearchpy.Transport` class and, subsequently, to the 39 | :class:`~opensearchpy.Connection` instances. 40 | """ 41 | self.transport = transport_class(_normalize_hosts(hosts), **kwargs) 42 | -------------------------------------------------------------------------------- /opensearchpy/helpers/errors.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | from typing import Any, List 29 | 30 | from ..exceptions import OpenSearchException 31 | 32 | 33 | class BulkIndexError(OpenSearchException): 34 | @property 35 | def errors(self) -> List[Any]: 36 | """List of errors from execution of the last chunk.""" 37 | return self.args[1] # type: ignore 38 | 39 | 40 | class ScanError(OpenSearchException): 41 | scroll_id: str 42 | 43 | def __init__(self, scroll_id: str, *args: Any, **kwargs: Any) -> None: 44 | super().__init__(*args, **kwargs) 45 | self.scroll_id = scroll_id 46 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | This document contains a list of maintainers in this repo. See [opensearch-project/.github/RESPONSIBILITIES.md](https://github.com/opensearch-project/.github/blob/main/RESPONSIBILITIES.md#maintainer-responsibilities) that explains what the role of maintainer means, what maintainers do in this and other repos, and how they should be doing it. If you're interested in contributing, and becoming a maintainer, see [CONTRIBUTING](CONTRIBUTING.md). 4 | 5 | ## Current Maintainers 6 | 7 | | Maintainer | GitHub ID | Affiliation | 8 | | ------------------------- | --------------------------------------------------- | ----------- | 9 | | Vacha Shah | [VachaShah](https://github.com/VachaShah) | Amazon | 10 | | Harsha Vamsi Kalluri | [harshavamsi](https://github.com/harshavamsi) | Amazon | 11 | | Aleksei Atavin | [axeoman](https://github.com/axeoman) | Aiven | 12 | | Denis Zalevskiy | [deztructor](https://github.com/deztructor) | Aiven | 13 | | Shephali Mittal | [Shephalimittal](https://github.com/Shephalimittal) | Amazon | 14 | | Sai Medhini Reddy Maryada | [saimedhi](https://github.com/saimedhi) | Amazon | 15 | | Florian Vazelle | [florianvazelle](https://github.com/florianvazelle) | harfanglab | 16 | 17 | ## Emeritus 18 | 19 | | Maintainer | GitHub ID | Affiliation | 20 | | ------------------ | --------------------------------------- | ----------- | 21 | | Daniel Doubrovkine | [dblock](https://github.com/dblock) | Independent | 22 | | Rushi Agrawal | [rushiagr](https://github.com/rushiagr) | Amazon | 23 | -------------------------------------------------------------------------------- /samples/security/roles.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # The OpenSearch Contributors require contributions made to 6 | # this file be licensed under the Apache-2.0 license or a 7 | # compatible open source license. 8 | # 9 | # Modifications Copyright OpenSearch Contributors. See 10 | # GitHub history for details. 11 | 12 | import os 13 | 14 | from opensearchpy import OpenSearch 15 | 16 | 17 | def main() -> None: 18 | """ 19 | A basic OpenSearch sample that create and manage roles. 20 | """ 21 | # connect to OpenSearch 22 | 23 | host = "localhost" 24 | port = 9200 25 | auth = ( 26 | "admin", 27 | os.getenv("OPENSEARCH_PASSWORD", "admin"), 28 | ) # For testing only. Don't store credentials in code. 29 | 30 | client = OpenSearch( 31 | hosts=[{"host": host, "port": port}], 32 | http_auth=auth, 33 | use_ssl=True, 34 | verify_certs=False, 35 | ssl_show_warn=False, 36 | ) 37 | 38 | # Create a Role 39 | 40 | role_name = "test-role" 41 | 42 | role_content = { 43 | "cluster_permissions": ["cluster_monitor"], 44 | "index_permissions": [ 45 | { 46 | "index_patterns": ["index", "test-*"], 47 | "allowed_actions": [ 48 | "data_access", 49 | "indices_monitor", 50 | ], 51 | } 52 | ], 53 | } 54 | 55 | response = client.security.create_role(role=role_name, body=role_content) 56 | print(response) 57 | 58 | # Get a Role 59 | 60 | role_name = "test-role" 61 | 62 | response = client.security.get_role(role=role_name) 63 | print(response) 64 | 65 | 66 | if __name__ == "__main__": 67 | main() 68 | -------------------------------------------------------------------------------- /samples/hello/unicode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # The OpenSearch Contributors require contributions made to 6 | # this file be licensed under the Apache-2.0 license or a 7 | # compatible open source license. 8 | # 9 | # Modifications Copyright OpenSearch Contributors. See 10 | # GitHub history for details. 11 | 12 | 13 | import os 14 | 15 | from opensearchpy import OpenSearch 16 | 17 | # connect to OpenSearch 18 | 19 | 20 | def main() -> None: 21 | """ 22 | An example showing how to create a synchronous connection to 23 | OpenSearch, create an index, index a document and search to 24 | return the document. 25 | """ 26 | host = "localhost" 27 | port = 9200 28 | auth = ( 29 | "admin", 30 | os.getenv("OPENSEARCH_PASSWORD", "admin"), 31 | ) # For testing only. Don't store credentials in code. 32 | 33 | client = OpenSearch( 34 | hosts=[{"host": host, "port": port}], 35 | http_auth=auth, 36 | use_ssl=True, 37 | verify_certs=False, 38 | ssl_show_warn=False, 39 | ) 40 | 41 | info = client.info() 42 | print(f"Welcome to {info['version']['distribution']} {info['version']['number']}!") 43 | 44 | index_name = "кино" 45 | index_create_result = client.indices.create(index=index_name) 46 | print(index_create_result) 47 | 48 | document = {"название": "Солярис", "автор": "Андрей Тарковский", "год": "2011"} 49 | id = "соларис@2011" 50 | doc_insert_result = client.index( 51 | index=index_name, body=document, id=id, refresh=True 52 | ) 53 | print(doc_insert_result) 54 | 55 | doc_delete_result = client.delete(index=index_name, id=id) 56 | print(doc_delete_result) 57 | 58 | index_delete_result = client.indices.delete(index=index_name) 59 | print(index_delete_result) 60 | 61 | 62 | if __name__ == "__main__": 63 | main() 64 | -------------------------------------------------------------------------------- /opensearchpy/metrics/metrics_events.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | import time 11 | from typing import Optional 12 | 13 | from events import Events 14 | 15 | from opensearchpy.metrics.metrics import Metrics 16 | 17 | 18 | class MetricsEvents(Metrics): 19 | """ 20 | The MetricsEvents class implements the Metrics abstract base class 21 | and tracks metrics such as start time, end time, and service time 22 | during request processing. 23 | """ 24 | 25 | @property 26 | def start_time(self) -> Optional[float]: 27 | return self._start_time 28 | 29 | @property 30 | def end_time(self) -> Optional[float]: 31 | return self._end_time 32 | 33 | @property 34 | def service_time(self) -> Optional[float]: 35 | return self._service_time 36 | 37 | def __init__(self) -> None: 38 | self.events = Events() 39 | self._start_time: Optional[float] = None 40 | self._end_time: Optional[float] = None 41 | self._service_time: Optional[float] = None 42 | 43 | # Subscribe to the request_start and request_end events 44 | self.events.request_start += self._on_request_start 45 | self.events.request_end += self._on_request_end 46 | 47 | def request_start(self) -> None: 48 | self.events.request_start() 49 | 50 | def _on_request_start(self) -> None: 51 | self._start_time = time.perf_counter() 52 | self._end_time = None 53 | self._service_time = None 54 | 55 | def request_end(self) -> None: 56 | self.events.request_end() 57 | 58 | def _on_request_end(self) -> None: 59 | self._end_time = time.perf_counter() 60 | if self._start_time is not None: 61 | self._service_time = self._end_time - self._start_time 62 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: CI 3 | 4 | on: [push, pull_request] 5 | 6 | jobs: 7 | lint: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout Repository 11 | uses: actions/checkout@v6 12 | - name: Set up Python 3.10 13 | uses: actions/setup-python@v6 14 | with: 15 | python-version: '3.10' 16 | - name: Install dependencies 17 | run: | 18 | python3.10 -m pip install nox 19 | - name: Lint the code 20 | run: nox -s lint 21 | 22 | generate: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - name: Checkout Repository 26 | uses: actions/checkout@v6 27 | - name: Set up Python 3.10 28 | uses: actions/setup-python@v6 29 | with: 30 | python-version: '3.10' 31 | - name: Install dependencies 32 | run: | 33 | python3.10 -m pip install nox 34 | - name: Run the api generator 35 | run: nox -s generate 36 | 37 | test-build-distribution: 38 | runs-on: ubuntu-latest 39 | steps: 40 | - name: Checkout Repository 41 | uses: actions/checkout@v6 42 | - name: Set up Python 3.10 43 | uses: actions/setup-python@v6 44 | with: 45 | python-version: '3.10' 46 | - name: Install build tools 47 | run: | 48 | python3.10 -m pip install --upgrade build 49 | - name: Build project for distribution 50 | run: | 51 | python3.10 -m build 52 | 53 | twine-check: 54 | runs-on: ubuntu-latest 55 | steps: 56 | - uses: actions/checkout@v6 57 | - name: Set up Python 58 | uses: actions/setup-python@v6 59 | with: 60 | python-version: "3.x" 61 | - name: Install dependencies 62 | run: | 63 | python -m pip install --upgrade pip 64 | python -m pip install setuptools wheel twine 65 | - name: Build source distribution 66 | run: python setup.py sdist bdist_wheel 67 | - name: Check with Twine 68 | working-directory: dist 69 | run: twine check * 70 | -------------------------------------------------------------------------------- /samples/bulk/bulk_array.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # The OpenSearch Contributors require contributions made to 6 | # this file be licensed under the Apache-2.0 license or a 7 | # compatible open source license. 8 | # 9 | # Modifications Copyright OpenSearch Contributors. See 10 | # GitHub history for details. 11 | 12 | 13 | import os 14 | from typing import Any 15 | 16 | from opensearchpy import OpenSearch 17 | 18 | 19 | def main() -> None: 20 | """demonstrates how to bulk load data into an index""" 21 | # connect to an instance of OpenSearch 22 | 23 | host = os.getenv("HOST", default="localhost") 24 | port = int(os.getenv("PORT", 9200)) 25 | auth = (os.getenv("USERNAME", "admin"), os.getenv("PASSWORD", "admin")) 26 | 27 | client = OpenSearch( 28 | hosts=[{"host": host, "port": port}], 29 | http_auth=auth, 30 | use_ssl=True, 31 | verify_certs=False, 32 | ssl_show_warn=False, 33 | ) 34 | 35 | # check whether an index exists 36 | index_name = "my-index" 37 | 38 | if not client.indices.exists(index=index_name): 39 | client.indices.create( 40 | index=index_name, 41 | body={ 42 | "mappings": { 43 | "properties": { 44 | "value": {"type": "float"}, 45 | } 46 | } 47 | }, 48 | ) 49 | 50 | # index data 51 | data: Any = [] 52 | for i in range(100): 53 | data.append({"index": {"_index": index_name, "_id": i}}) 54 | data.append({"value": i}) 55 | 56 | rc = client.bulk(body=data) # pylint: disable=invalid-name 57 | if rc["errors"]: 58 | print("There were errors:") 59 | for item in rc["items"]: 60 | print(f"{item['index']['status']}: {item['index']['error']['type']}") 61 | else: 62 | print(f"Bulk-inserted {len(rc['items'])} items.") 63 | 64 | # delete index 65 | client.indices.delete(index=index_name) 66 | 67 | 68 | if __name__ == "__main__": 69 | main() 70 | -------------------------------------------------------------------------------- /test_opensearchpy/test_server/test_helpers/test_count.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | from typing import Any 28 | 29 | from opensearchpy.helpers.search import Q, Search 30 | 31 | 32 | def test_count_all(data_client: Any) -> None: 33 | s = Search(using=data_client).index("git") 34 | assert 53 == s.count() 35 | 36 | 37 | def test_count_prefetch(data_client: Any, mocker: Any) -> None: 38 | mocker.spy(data_client, "count") 39 | 40 | search = Search(using=data_client).index("git") 41 | search.execute() 42 | assert search.count() == 53 43 | assert data_client.count.call_count == 0 44 | 45 | search._response.hits.total.relation = "gte" 46 | assert search.count() == 53 47 | assert data_client.count.call_count == 1 48 | 49 | 50 | def test_count_filter(data_client: Any) -> None: 51 | s = Search(using=data_client).index("git").filter(~Q("exists", field="parent_shas")) 52 | # initial commit + repo document 53 | assert 2 == s.count() 54 | -------------------------------------------------------------------------------- /test_opensearchpy/test_exceptions.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | from opensearchpy.exceptions import TransportError 29 | 30 | from .test_cases import TestCase 31 | 32 | 33 | class TestTransformError(TestCase): 34 | def test_transform_error_parse_with_error_reason(self) -> None: 35 | e = TransportError( 36 | 500, 37 | "InternalServerError", 38 | {"error": {"root_cause": [{"type": "error", "reason": "error reason"}]}}, 39 | ) 40 | 41 | self.assertEqual( 42 | str(e), "TransportError(500, 'InternalServerError', 'error reason')" 43 | ) 44 | 45 | def test_transform_error_parse_with_error_string(self) -> None: 46 | e = TransportError( 47 | 500, "InternalServerError", {"error": "something error message"} 48 | ) 49 | 50 | self.assertEqual( 51 | str(e), 52 | "TransportError(500, 'InternalServerError', 'something error message')", 53 | ) 54 | -------------------------------------------------------------------------------- /samples/bulk/bulk_ld.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # The OpenSearch Contributors require contributions made to 6 | # this file be licensed under the Apache-2.0 license or a 7 | # compatible open source license. 8 | # 9 | # Modifications Copyright OpenSearch Contributors. See 10 | # GitHub history for details. 11 | 12 | 13 | import json 14 | import os 15 | 16 | from opensearchpy import OpenSearch 17 | 18 | 19 | def main() -> None: 20 | """ 21 | bulk index 100 items and then delete the index 22 | """ 23 | # connect to an instance of OpenSearch 24 | 25 | host = os.getenv("HOST", default="localhost") 26 | port = int(os.getenv("PORT", 9200)) 27 | auth = (os.getenv("USERNAME", "admin"), os.getenv("PASSWORD", "admin")) 28 | 29 | client = OpenSearch( 30 | hosts=[{"host": host, "port": port}], 31 | http_auth=auth, 32 | use_ssl=True, 33 | verify_certs=False, 34 | ssl_show_warn=False, 35 | ) 36 | 37 | # check whether an index exists 38 | index_name = "my-index" 39 | 40 | if not client.indices.exists(index=index_name): 41 | client.indices.create( 42 | index=index_name, 43 | body={ 44 | "mappings": { 45 | "properties": { 46 | "value": {"type": "float"}, 47 | } 48 | } 49 | }, 50 | ) 51 | 52 | # index data 53 | data = "" 54 | for i in range(100): 55 | data += json.dumps({"index": {"_index": index_name, "_id": i}}) + "\n" 56 | data += json.dumps({"value": i}) + "\n" 57 | 58 | rc = client.bulk(body=data) # pylint: disable=invalid-name 59 | if rc["errors"]: 60 | print("There were errors:") 61 | for item in rc["items"]: 62 | print(f"{item['index']['status']}: {item['index']['error']['type']}") 63 | else: 64 | print(f"Bulk-inserted {len(rc['items'])} items.") 65 | 66 | # delete index 67 | client.indices.delete(index=index_name) 68 | 69 | 70 | if __name__ == "__main__": 71 | main() 72 | -------------------------------------------------------------------------------- /.github/workflows/update_api.yml: -------------------------------------------------------------------------------- 1 | name: Update API 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: "30 3 * * *" 6 | jobs: 7 | update-api: 8 | if: ${{ github.repository == 'opensearch-project/opensearch-py' }} 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: write 12 | pull-requests: write 13 | steps: 14 | - uses: actions/checkout@v6 15 | with: 16 | submodules: recursive 17 | fetch-depth: 0 18 | - name: Config git to rebase 19 | run: git config --global pull.rebase true 20 | - name: Set up Python 3.10 21 | uses: actions/setup-python@v6 22 | with: 23 | python-version: '3.10' 24 | - name: Install dependencies 25 | run: | 26 | python3.10 -m pip install nox 27 | - name: Generate API 28 | run: nox -s generate 29 | - name: Get current date 30 | id: date 31 | run: echo "::set-output name=date::$(date +'%Y-%m-%d')" 32 | - name: GitHub App token 33 | id: github_app_token 34 | uses: tibdex/github-app-token@v2.1.0 35 | with: 36 | app_id: ${{ secrets.APP_ID }} 37 | private_key: ${{ secrets.APP_PRIVATE_KEY }} 38 | installation_id: 22958780 39 | - name: Create pull request 40 | id: cpr 41 | uses: peter-evans/create-pull-request@v7 42 | with: 43 | token: ${{ steps.github_app_token.outputs.token }} 44 | commit-message: Updated opensearch-py to reflect the latest OpenSearch API spec (${{ steps.date.outputs.date }}) 45 | title: Updated opensearch-py to reflect the latest OpenSearch API spec 46 | body: | 47 | Updated [opensearch-py](https://github.com/opensearch-project/opensearch-py) to reflect the latest [OpenSearch API spec](https://github.com/opensearch-project/opensearch-api-specification/releases/download/main-latest/opensearch-openapi.yaml) 48 | Date: ${{ steps.date.outputs.date }} 49 | branch: automated-api-update 50 | base: main 51 | signoff: true 52 | labels: | 53 | autocut -------------------------------------------------------------------------------- /utils/templates/base: -------------------------------------------------------------------------------- 1 | 2 | @query_params({{ api.query_params|map("tojson")|join(", ")}}) 3 | async def {{ api.name }}(self, *, {% block func_params %}{% include "func_params" %}{% endblock %}) -> Any: 4 | """ 5 | {% if api.description %} 6 | {{ api.description|replace("\n", " ")|wordwrap(wrapstring="\n ") }} 7 | {% endif %} 8 | 9 | {% if api.doc_url %} 10 | 11 | `<{{ api.doc_url }}>`_ 12 | {% endif %} 13 | {% if api.stability != "stable" %} 14 | 15 | .. warning:: 16 | 17 | This API is **{{ api.stability }}** so may include breaking changes 18 | or be removed in a future version 19 | {% endif %} 20 | {% if api.params|list|length %} 21 | 22 | {% for p, info in api.params %} 23 | {% if info.description %} 24 | {% filter wordwrap(72, wrapstring="\n ") %} 25 | :arg {{ p }}{% if info.deprecated and info.deprecation_message is defined %} (Deprecated: {{ info['deprecation_message'][:-1] }}.){% endif %}: {{ info.description }} {% if info.options and "Valid values" not in info.description %}Valid choices are {{ info.options|join(", ") }}.{% endif %} 26 | {% if info.default is defined %}{% if info.default is not none %}{% if info.default is sameas(false) %}Default is false.{% else %}Default is {{ info.default }}.{% endif %}{% endif %}{% endif %} 27 | {% endfilter %} 28 | 29 | {% endif %} 30 | {% endfor %} 31 | {% endif %} 32 | """ 33 | {% if api.deprecation_message %} 34 | from warnings import warn 35 | warn("Deprecated: {{ api.deprecation_message }}") 36 | {% endif %} 37 | {% include "substitutions" %} 38 | {% include "required" %} 39 | {% if api.body.serialize %} 40 | body = _bulk_body(self.transport.serializer, body) 41 | {% endif %} 42 | {% block request %} 43 | return await self.transport.perform_request("{{ api.method }}", {% include "url" %}, params=params, headers=headers{% if api.body %}, body=body{% endif %}) 44 | {% endblock %} 45 | -------------------------------------------------------------------------------- /test_opensearchpy/test_server/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | from typing import Any 29 | from unittest import SkipTest 30 | 31 | from opensearchpy.helpers import test 32 | from opensearchpy.helpers.test import OpenSearchTestCase as BaseTestCase 33 | 34 | # pylint: disable=invalid-name 35 | CLIENT: Any = None 36 | 37 | 38 | def get_client(**kwargs: Any) -> Any: 39 | global CLIENT 40 | if CLIENT is False: 41 | raise SkipTest("No client is available") 42 | if CLIENT is not None and not kwargs: 43 | return CLIENT 44 | 45 | try: 46 | new_client = test.get_test_client(**kwargs) 47 | except SkipTest: 48 | CLIENT = False 49 | raise 50 | 51 | if not kwargs: 52 | CLIENT = new_client 53 | 54 | return new_client 55 | 56 | 57 | def setup_module() -> None: 58 | get_client() 59 | 60 | 61 | class OpenSearchTestCase(BaseTestCase): 62 | @staticmethod 63 | def _get_client(**kwargs: Any) -> Any: 64 | return get_client(**kwargs) 65 | -------------------------------------------------------------------------------- /test_opensearchpy/test_client/test_http.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | 11 | from test_opensearchpy.test_cases import OpenSearchTestCase 12 | 13 | 14 | class TestHttp(OpenSearchTestCase): 15 | def test_http_get(self) -> None: 16 | self.client.http.get("/") 17 | self.assert_call_count_equals(1) 18 | self.assertEqual([(None, None, None)], self.assert_url_called("GET", "/", 1)) 19 | 20 | def test_http_head(self) -> None: 21 | self.client.http.head("/") 22 | self.assert_call_count_equals(1) 23 | self.assertEqual([(None, None, None)], self.assert_url_called("HEAD", "/", 1)) 24 | 25 | def test_http_put(self) -> None: 26 | self.client.http.put("/xyz", headers={"X": "Y"}, body="body") 27 | self.assert_call_count_equals(1) 28 | self.assertEqual( 29 | [(None, {"X": "Y"}, "body")], self.assert_url_called("PUT", "/xyz", 1) 30 | ) 31 | 32 | def test_http_post(self) -> None: 33 | self.client.http.post("/xyz", headers={"X": "Y"}, body="body") 34 | self.assert_call_count_equals(1) 35 | self.assertEqual( 36 | [(None, {"X": "Y"}, "body")], self.assert_url_called("POST", "/xyz", 1) 37 | ) 38 | 39 | def test_http_post_with_params(self) -> None: 40 | self.client.http.post( 41 | "/xyz", headers={"X": "Y"}, params={"A": "B"}, body="body" 42 | ) 43 | self.assert_call_count_equals(1) 44 | self.assertEqual( 45 | [({"A": "B"}, {"X": "Y"}, "body")], 46 | self.assert_url_called("POST", "/xyz", 1), 47 | ) 48 | 49 | def test_http_delete(self) -> None: 50 | self.client.http.delete("/xyz", headers={"X": "Y"}, body="body") 51 | self.assert_call_count_equals(1) 52 | self.assertEqual( 53 | [(None, {"X": "Y"}, "body")], self.assert_url_called("DELETE", "/xyz", 1) 54 | ) 55 | -------------------------------------------------------------------------------- /opensearchpy/plugins/ubi.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | # ------------------------------------------------------------------------------------------ 11 | # THIS CODE IS AUTOMATICALLY GENERATED AND MANUAL EDITS WILL BE LOST 12 | # 13 | # To contribute, kindly make modifications in the opensearch-py client generator 14 | # or in the OpenSearch API specification, and run `nox -rs generate`. See DEVELOPER_GUIDE.md 15 | # and https://github.com/opensearch-project/opensearch-api-specification for details. 16 | # -----------------------------------------------------------------------------------------+ 17 | 18 | 19 | from typing import Any 20 | 21 | from ..client.utils import NamespacedClient, query_params 22 | 23 | 24 | class UbiClient(NamespacedClient): 25 | @query_params("error_trace", "filter_path", "human", "pretty", "source") 26 | def initialize( 27 | self, 28 | *, 29 | params: Any = None, 30 | headers: Any = None, 31 | ) -> Any: 32 | """ 33 | Initializes the UBI indexes. 34 | 35 | 36 | :arg error_trace: Whether to include the stack trace of returned 37 | errors. Default is false. 38 | :arg filter_path: A comma-separated list of filters used to 39 | filter the response. Use wildcards to match any field or part of a 40 | field's name. To exclude fields, use `-`. 41 | :arg human: Whether to return human-readable values for 42 | statistics. Default is false. 43 | :arg pretty: Whether to pretty-format the returned JSON 44 | response. Default is false. 45 | :arg source: The URL-encoded request definition. Useful for 46 | libraries that do not accept a request body for non-POST requests. 47 | """ 48 | return self.transport.perform_request( 49 | "POST", "/_plugins/ubi/initialize", params=params, headers=headers 50 | ) 51 | -------------------------------------------------------------------------------- /test_opensearchpy/test_async/test_server/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | import os 29 | from unittest import IsolatedAsyncioTestCase 30 | 31 | from opensearchpy._async.helpers.test import get_test_client 32 | from opensearchpy.connection.async_connections import add_connection 33 | 34 | from ...utils import wipe_cluster 35 | 36 | 37 | class AsyncOpenSearchTestCase(IsolatedAsyncioTestCase): 38 | async def asyncSetUp( 39 | self, 40 | ) -> None: 41 | # pylint: disable=invalid-name,missing-function-docstring 42 | password = os.environ.get("OPENSEARCH_INITIAL_ADMIN_PASSWORD", "admin") 43 | self.client = await get_test_client( 44 | verify_certs=False, http_auth=("admin", password) 45 | ) 46 | await add_connection("default", self.client) 47 | 48 | async def asyncTearDown( 49 | self, 50 | ) -> None: 51 | # pylint: disable=invalid-name,missing-function-docstring 52 | wipe_cluster(self.client) 53 | if self.client: 54 | await self.client.close() 55 | -------------------------------------------------------------------------------- /samples/json/json_hello.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # The OpenSearch Contributors require contributions made to 6 | # this file be licensed under the Apache-2.0 license or a 7 | # compatible open source license. 8 | # 9 | # Modifications Copyright OpenSearch Contributors. See 10 | # GitHub history for details. 11 | 12 | 13 | import os 14 | 15 | from opensearchpy import OpenSearch 16 | 17 | 18 | def main() -> None: 19 | """ 20 | demonstrates how to index a document using a dict 21 | """ 22 | # connect to OpenSearch 23 | 24 | host = "localhost" 25 | port = 9200 26 | auth = ( 27 | "admin", 28 | os.getenv("OPENSEARCH_PASSWORD", "admin"), 29 | ) # For testing only. Don't store credentials in code. 30 | 31 | client = OpenSearch( 32 | hosts=[{"host": host, "port": port}], 33 | http_auth=auth, 34 | use_ssl=True, 35 | verify_certs=False, 36 | ssl_show_warn=False, 37 | ) 38 | 39 | info = client.http.get("/") 40 | print(f"Welcome to {info['version']['distribution']} {info['version']['number']}!") 41 | 42 | # create an index 43 | 44 | index_name = "movies" 45 | 46 | index_body = {"settings": {"index": {"number_of_shards": 4}}} 47 | 48 | print(client.http.put(f"/{index_name}", body=index_body)) 49 | 50 | # add a document to the index 51 | 52 | document = {"title": "Moneyball", "director": "Bennett Miller", "year": "2011"} 53 | 54 | doc_id = "1" 55 | 56 | print(client.http.put(f"/{index_name}/_doc/{doc_id}?refresh=true", body=document)) 57 | 58 | # search for a document 59 | 60 | user_query = "miller" 61 | 62 | query = { 63 | "size": 5, 64 | "query": { 65 | "multi_match": {"query": user_query, "fields": ["title^2", "director"]} 66 | }, 67 | } 68 | 69 | print(client.http.post(f"/{index_name}/_search", body=query)) 70 | 71 | # delete the document 72 | 73 | print(client.http.delete(f"/{index_name}/_doc/{doc_id}")) 74 | 75 | # delete the index 76 | 77 | print(client.http.delete(f"/{index_name}")) 78 | 79 | 80 | if __name__ == "__main__": 81 | main() 82 | -------------------------------------------------------------------------------- /opensearchpy/_async/plugins/ubi.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | # ------------------------------------------------------------------------------------------ 11 | # THIS CODE IS AUTOMATICALLY GENERATED AND MANUAL EDITS WILL BE LOST 12 | # 13 | # To contribute, kindly make modifications in the opensearch-py client generator 14 | # or in the OpenSearch API specification, and run `nox -rs generate`. See DEVELOPER_GUIDE.md 15 | # and https://github.com/opensearch-project/opensearch-api-specification for details. 16 | # -----------------------------------------------------------------------------------------+ 17 | 18 | 19 | from typing import Any 20 | 21 | from ..client.utils import NamespacedClient, query_params 22 | 23 | 24 | class UbiClient(NamespacedClient): 25 | @query_params("error_trace", "filter_path", "human", "pretty", "source") 26 | async def initialize( 27 | self, 28 | *, 29 | params: Any = None, 30 | headers: Any = None, 31 | ) -> Any: 32 | """ 33 | Initializes the UBI indexes. 34 | 35 | 36 | :arg error_trace: Whether to include the stack trace of returned 37 | errors. Default is false. 38 | :arg filter_path: A comma-separated list of filters used to 39 | filter the response. Use wildcards to match any field or part of a 40 | field's name. To exclude fields, use `-`. 41 | :arg human: Whether to return human-readable values for 42 | statistics. Default is false. 43 | :arg pretty: Whether to pretty-format the returned JSON 44 | response. Default is false. 45 | :arg source: The URL-encoded request definition. Useful for 46 | libraries that do not accept a request body for non-POST requests. 47 | """ 48 | return await self.transport.perform_request( 49 | "POST", "/_plugins/ubi/initialize", params=params, headers=headers 50 | ) 51 | -------------------------------------------------------------------------------- /opensearchpy/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | from .._async.helpers.actions import ( 29 | async_bulk, 30 | async_reindex, 31 | async_scan, 32 | async_streaming_bulk, 33 | ) 34 | from .actions import ( 35 | _chunk_actions, 36 | _process_bulk_chunk, 37 | bulk, 38 | expand_action, 39 | parallel_bulk, 40 | reindex, 41 | scan, 42 | streaming_bulk, 43 | ) 44 | from .asyncsigner import AWSV4SignerAsyncAuth 45 | from .errors import BulkIndexError, ScanError 46 | from .signer import AWSV4SignerAuth, RequestsAWSV4SignerAuth, Urllib3AWSV4SignerAuth 47 | 48 | __all__ = [ 49 | "BulkIndexError", 50 | "ScanError", 51 | "expand_action", 52 | "streaming_bulk", 53 | "bulk", 54 | "parallel_bulk", 55 | "scan", 56 | "reindex", 57 | "_chunk_actions", 58 | "_process_bulk_chunk", 59 | "AWSV4SignerAuth", 60 | "AWSV4SignerAsyncAuth", 61 | "RequestsAWSV4SignerAuth", 62 | "Urllib3AWSV4SignerAuth", 63 | "async_scan", 64 | "async_bulk", 65 | "async_reindex", 66 | "async_streaming_bulk", 67 | ] 68 | -------------------------------------------------------------------------------- /.ci/functions/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Shared cleanup routines between different steps 4 | # 5 | # Please source .ci/functions/imports.sh as a whole not just this file 6 | # 7 | # Version 1.0.0 8 | # - Initial version after refactor 9 | 10 | function cleanup_volume { 11 | if [[ "$(docker volume ls -q -f name=$1)" ]]; then 12 | echo -e "\033[34;1mINFO:\033[0m Removing volume $1\033[0m" 13 | (docker volume rm "$1") || true 14 | fi 15 | } 16 | function container_running { 17 | if [[ "$(docker ps -q -f name=$1)" ]]; then 18 | return 0; 19 | else return 1; 20 | fi 21 | } 22 | function cleanup_node { 23 | if container_running "$1"; then 24 | echo -e "\033[34;1mINFO:\033[0m Removing container $1\033[0m" 25 | (docker container rm --force --volumes "$1") || true 26 | fi 27 | if [[ -n "$1" ]]; then 28 | echo -e "\033[34;1mINFO:\033[0m Removing volume $1-rest-test-data\033[0m" 29 | cleanup_volume "$1-rest-test-data" 30 | fi 31 | } 32 | function cleanup_network { 33 | if [[ "$(docker network ls -q -f name=$1)" ]]; then 34 | echo -e "\033[34;1mINFO:\033[0m Removing network $1\033[0m" 35 | (docker network rm "$1") || true 36 | fi 37 | } 38 | 39 | function cleanup_trap { 40 | status=$? 41 | set +x 42 | if [[ "$DETACH" != "true" ]]; then 43 | echo -e "\033[34;1mINFO:\033[0m clean the network if not detached (start and exit)\033[0m" 44 | cleanup_all_in_network "$1" 45 | fi 46 | # status is 0 or SIGINT 47 | if [[ "$status" == "0" || "$status" == "130" ]]; then 48 | echo -e "\n\033[32;1mSUCCESS run-tests\033[0m" 49 | exit 0 50 | else 51 | echo -e "\n\033[31;1mFAILURE during run-tests\033[0m" 52 | exit ${status} 53 | fi 54 | }; 55 | function cleanup_all_in_network { 56 | 57 | if [[ -z "$(docker network ls -q -f name="^$1\$")" ]]; then 58 | echo -e "\033[34;1mINFO:\033[0m $1 is already deleted\033[0m" 59 | return 0 60 | fi 61 | containers=$(docker network inspect -f '{{ range $key, $value := .Containers }}{{ printf "%s\n" .Name}}{{ end }}' $1) 62 | while read -r container; do 63 | cleanup_node "$container" 64 | done <<< "$containers" 65 | cleanup_network $1 66 | echo -e "\033[32;1mSUCCESS:\033[0m Cleaned up and exiting\033[0m" 67 | }; 68 | -------------------------------------------------------------------------------- /opensearchpy/_async/_extra_imports.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | # type: ignore 29 | 30 | # This file exists for the sole reason of making mypy not 31 | # complain about type issues to do with 'aiohttp' and 'yarl'. 32 | # We're in a catch-22 situation: 33 | # - If we use 'type: ignore' on 'import aiohttp' and it's not installed 34 | # mypy will complain that the annotation is unnecessary. 35 | # - If we don't use 'type: ignore' on 'import aiohttp' and it 36 | # it's not installed mypy will complain that it can't find 37 | # type hints for aiohttp. 38 | # So to make mypy happy we move all our 'extra' imports here 39 | # and add a global 'type: ignore' which mypy never complains 40 | # about being unnecessary. 41 | 42 | import aiohttp 43 | import aiohttp.client_exceptions as aiohttp_exceptions 44 | 45 | # We do this because we don't explicitly require 'yarl' 46 | # within our [async] extra any more. 47 | # See AIOHttpConnection.request() for more information why. 48 | try: 49 | import yarl 50 | except ImportError: 51 | yarl = False 52 | 53 | __all__ = ["aiohttp", "aiohttp_exceptions", "yarl"] 54 | -------------------------------------------------------------------------------- /opensearchpy/client/insights.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | # ------------------------------------------------------------------------------------------ 11 | # THIS CODE IS AUTOMATICALLY GENERATED AND MANUAL EDITS WILL BE LOST 12 | # 13 | # To contribute, kindly make modifications in the opensearch-py client generator 14 | # or in the OpenSearch API specification, and run `nox -rs generate`. See DEVELOPER_GUIDE.md 15 | # and https://github.com/opensearch-project/opensearch-api-specification for details. 16 | # -----------------------------------------------------------------------------------------+ 17 | 18 | 19 | from typing import Any 20 | 21 | from .utils import NamespacedClient, query_params 22 | 23 | 24 | class InsightsClient(NamespacedClient): 25 | @query_params("error_trace", "filter_path", "human", "pretty", "source") 26 | def top_queries( 27 | self, 28 | *, 29 | params: Any = None, 30 | headers: Any = None, 31 | ) -> Any: 32 | """ 33 | Retrieves the top queries based on the given metric type (latency, CPU, or 34 | memory). 35 | 36 | 37 | :arg error_trace: Whether to include the stack trace of returned 38 | errors. Default is false. 39 | :arg filter_path: A comma-separated list of filters used to 40 | filter the response. Use wildcards to match any field or part of a 41 | field's name. To exclude fields, use `-`. 42 | :arg human: Whether to return human-readable values for 43 | statistics. Default is false. 44 | :arg pretty: Whether to pretty-format the returned JSON 45 | response. Default is false. 46 | :arg source: The URL-encoded request definition. Useful for 47 | libraries that do not accept a request body for non-POST requests. 48 | """ 49 | return self.transport.perform_request( 50 | "GET", "/_insights/top_queries", params=params, headers=headers 51 | ) 52 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release drafter 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | draft-a-release: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: write 13 | issues: write 14 | steps: 15 | - name: Checkout Repository 16 | uses: actions/checkout@v6 17 | - id: get_approvers 18 | run: | 19 | echo "approvers=$(cat .github/CODEOWNERS | grep @ | tr -d '* ' | sed 's/@/,/g' | sed 's/,//1')" >> $GITHUB_OUTPUT 20 | - uses: trstringer/manual-approval@v1 21 | with: 22 | secret: ${{ github.TOKEN }} 23 | approvers: ${{ steps.get_approvers.outputs.approvers }} 24 | minimum-approvals: 1 25 | issue-title: 'Release opensearch-py' 26 | issue-body: "Please approve or deny the release of opensearch-py. **Tag**: ${{ github.ref_name }} **Commit**: ${{ github.sha }}" 27 | exclude-workflow-initiator-as-approver: true 28 | - name: Set up Python 3 29 | uses: actions/setup-python@v6 30 | with: 31 | python-version: '3.x' 32 | - name: Install build tools 33 | run: | 34 | python -m pip install --upgrade build 35 | - name: Build project for distribution 36 | run: | 37 | python -m build 38 | - name: upload windows dists 39 | uses: actions/upload-artifact@v5 40 | with: 41 | name: release-dists 42 | path: dist/ 43 | 44 | pypi-publish: 45 | runs-on: ubuntu-latest 46 | needs: 47 | - draft-a-release 48 | permissions: 49 | id-token: write 50 | contents: write 51 | steps: 52 | - name: Retrieve release distributions 53 | uses: actions/download-artifact@v6 54 | with: 55 | name: release-dists 56 | path: dist 57 | - name: Generate the artifacts 58 | run: | 59 | tar -zvcf artifacts.tar.gz dist 60 | - name: Publish package distributions to PyPI 61 | uses: pypa/gh-action-pypi-publish@release/v1 62 | - name: Release 63 | uses: softprops/action-gh-release@v2 64 | with: 65 | generate_release_notes: true 66 | files: | 67 | artifacts.tar.gz 68 | -------------------------------------------------------------------------------- /test_opensearchpy/test_async/test_http.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | import pytest 11 | from _pytest.mark.structures import MarkDecorator 12 | 13 | from .test_client import OpenSearchTestCaseWithDummyTransport 14 | 15 | pytestmark: MarkDecorator = pytest.mark.asyncio 16 | 17 | 18 | class TestHttpClient(OpenSearchTestCaseWithDummyTransport): 19 | async def test_head(self) -> None: 20 | await self.client.http.head("/") 21 | self.assert_call_count_equals(1) 22 | assert [(None, None, None)] == self.assert_url_called("HEAD", "/", 1) 23 | 24 | async def test_get(self) -> None: 25 | await self.client.http.get("/") 26 | self.assert_call_count_equals(1) 27 | assert [(None, None, None)] == self.assert_url_called("GET", "/", 1) 28 | 29 | async def test_put(self) -> None: 30 | await self.client.http.put(url="/xyz", params={"X": "Y"}, body="body") 31 | self.assert_call_count_equals(1) 32 | assert [({"X": "Y"}, None, "body")] == self.assert_url_called("PUT", "/xyz", 1) 33 | 34 | async def test_post(self) -> None: 35 | await self.client.http.post(url="/xyz", params={"X": "Y"}, body="body") 36 | self.assert_call_count_equals(1) 37 | assert [({"X": "Y"}, None, "body")] == self.assert_url_called("POST", "/xyz", 1) 38 | 39 | async def test_post_with_headers(self) -> None: 40 | await self.client.http.post( 41 | url="/xyz", headers={"A": "B"}, params={"X": "Y"}, body="body" 42 | ) 43 | self.assert_call_count_equals(1) 44 | assert [({"X": "Y"}, {"A": "B"}, "body")] == self.assert_url_called( 45 | "POST", "/xyz", 1 46 | ) 47 | 48 | async def test_delete(self) -> None: 49 | await self.client.http.delete(url="/xyz", params={"X": "Y"}, body="body") 50 | self.assert_call_count_equals(1) 51 | assert [({"X": "Y"}, None, "body")] == self.assert_url_called( 52 | "DELETE", "/xyz", 1 53 | ) 54 | -------------------------------------------------------------------------------- /opensearchpy/_async/client/insights.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | # ------------------------------------------------------------------------------------------ 11 | # THIS CODE IS AUTOMATICALLY GENERATED AND MANUAL EDITS WILL BE LOST 12 | # 13 | # To contribute, kindly make modifications in the opensearch-py client generator 14 | # or in the OpenSearch API specification, and run `nox -rs generate`. See DEVELOPER_GUIDE.md 15 | # and https://github.com/opensearch-project/opensearch-api-specification for details. 16 | # -----------------------------------------------------------------------------------------+ 17 | 18 | 19 | from typing import Any 20 | 21 | from .utils import NamespacedClient, query_params 22 | 23 | 24 | class InsightsClient(NamespacedClient): 25 | @query_params("error_trace", "filter_path", "human", "pretty", "source") 26 | async def top_queries( 27 | self, 28 | *, 29 | params: Any = None, 30 | headers: Any = None, 31 | ) -> Any: 32 | """ 33 | Retrieves the top queries based on the given metric type (latency, CPU, or 34 | memory). 35 | 36 | 37 | :arg error_trace: Whether to include the stack trace of returned 38 | errors. Default is false. 39 | :arg filter_path: A comma-separated list of filters used to 40 | filter the response. Use wildcards to match any field or part of a 41 | field's name. To exclude fields, use `-`. 42 | :arg human: Whether to return human-readable values for 43 | statistics. Default is false. 44 | :arg pretty: Whether to pretty-format the returned JSON 45 | response. Default is false. 46 | :arg source: The URL-encoded request definition. Useful for 47 | libraries that do not accept a request body for non-POST requests. 48 | """ 49 | return await self.transport.perform_request( 50 | "GET", "/_insights/top_queries", params=params, headers=headers 51 | ) 52 | -------------------------------------------------------------------------------- /samples/hello/unicode_async.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # The OpenSearch Contributors require contributions made to 6 | # this file be licensed under the Apache-2.0 license or a 7 | # compatible open source license. 8 | # 9 | # Modifications Copyright OpenSearch Contributors. See 10 | # GitHub history for details. 11 | 12 | 13 | import asyncio 14 | import os 15 | 16 | from opensearchpy import AsyncOpenSearch 17 | 18 | 19 | async def main() -> None: 20 | """ 21 | An example showing how to create an asynchronous connection 22 | to OpenSearch, create an index, index a document and 23 | search to return the document. 24 | """ 25 | # connect to OpenSearch 26 | host = "localhost" 27 | port = 9200 28 | auth = ( 29 | "admin", 30 | os.getenv("OPENSEARCH_PASSWORD", "admin"), 31 | ) # For testing only. Don't store credentials in code. 32 | 33 | client = AsyncOpenSearch( 34 | hosts=[{"host": host, "port": port}], 35 | http_auth=auth, 36 | use_ssl=True, 37 | verify_certs=False, 38 | ssl_show_warn=False, 39 | ) 40 | 41 | try: 42 | info = await client.info() 43 | print( 44 | f"Welcome to {info['version']['distribution']} {info['version']['number']}!" 45 | ) 46 | 47 | index_name = "кино" 48 | index_create_result = await client.indices.create(index=index_name) 49 | print(index_create_result) 50 | 51 | document = {"название": "Солярис", "автор": "Андрей Тарковский", "год": "2011"} 52 | id = "соларис@2011" 53 | doc_insert_result = await client.index( 54 | index=index_name, body=document, id=id, refresh=True 55 | ) 56 | print(doc_insert_result) 57 | 58 | doc_delete_result = await client.delete(index=index_name, id=id) 59 | print(doc_delete_result) 60 | 61 | index_delete_result = await client.indices.delete(index=index_name) 62 | print(index_delete_result) 63 | 64 | finally: 65 | await client.close() 66 | 67 | 68 | if __name__ == "__main__": 69 | loop = asyncio.new_event_loop() 70 | asyncio.set_event_loop(loop) 71 | loop.run_until_complete(main()) 72 | loop.close() 73 | -------------------------------------------------------------------------------- /samples/snapshot/snapshot_sample.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # The OpenSearch Contributors require contributions made to 6 | # this file be licensed under the Apache-2.0 license or a 7 | # compatible open source license. 8 | # 9 | # Modifications Copyright OpenSearch Contributors. See 10 | # GitHub history for details. 11 | 12 | import os 13 | import tempfile 14 | 15 | from opensearchpy import OpenSearch 16 | 17 | # connect to OpenSearch 18 | 19 | HOST = "localhost" 20 | PORT = 9200 21 | auth = ( 22 | "admin", 23 | os.getenv("OPENSEARCH_PASSWORD", "admin"), 24 | ) # For testing only. Don't store credentials in code. 25 | 26 | client = OpenSearch( 27 | hosts=[{"host": HOST, "port": PORT}], 28 | http_auth=auth, 29 | use_ssl=True, 30 | verify_certs=False, 31 | ssl_show_warn=False, 32 | ) 33 | 34 | # Create an index 35 | 36 | INDEX_NAME = "test-snapshot" 37 | client.indices.create(index=INDEX_NAME) 38 | 39 | # Create a temporary directory for the snapshot repository 40 | temp_repo = tempfile.TemporaryDirectory() 41 | TEMP_REPO_LOCATION = "/usr/share/opensearch/backups" 42 | 43 | # Define the repository body with the temporary location 44 | repo_body = { 45 | "type": "fs", # Replace 'fs' with the appropriate repository type 46 | "settings": { 47 | "location": TEMP_REPO_LOCATION, # Replace with the desired repository location 48 | }, 49 | } 50 | 51 | REPOSITORY_NAME = "my_repository" 52 | response = client.snapshot.create_repository(repository=REPOSITORY_NAME, body=repo_body) 53 | 54 | print(response) 55 | 56 | # Create a snapshot 57 | 58 | SNAPSHOT_NAME = "my_snapshot" 59 | response = client.snapshot.create( 60 | repository=REPOSITORY_NAME, snapshot=SNAPSHOT_NAME, body={"indices": INDEX_NAME} 61 | ) 62 | 63 | print(response) 64 | 65 | # Get Snapshot Information 66 | 67 | snapshot_info = client.snapshot.get(repository=REPOSITORY_NAME, snapshot=SNAPSHOT_NAME) 68 | 69 | print(snapshot_info) 70 | 71 | # Clean up - Delete Snapshot and Repository 72 | 73 | client.snapshot.delete(repository=REPOSITORY_NAME, snapshot=SNAPSHOT_NAME) 74 | client.snapshot.delete_repository(repository=REPOSITORY_NAME) 75 | 76 | # Clean up - Delete Index 77 | 78 | client.indices.delete(index=INDEX_NAME) 79 | -------------------------------------------------------------------------------- /test_opensearchpy/test_server/test_helpers/test_analysis.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | from typing import Any 28 | 29 | from opensearchpy import analyzer, token_filter, tokenizer 30 | 31 | 32 | def test_simulate_with_just__builtin_tokenizer(client: Any) -> None: 33 | a = analyzer("my-analyzer", tokenizer="keyword") 34 | tokens = a.simulate("Hello World!", using=client).tokens 35 | 36 | assert len(tokens) == 1 37 | assert tokens[0].token == "Hello World!" 38 | 39 | 40 | def test_simulate_complex(client: Any) -> None: 41 | a = analyzer( 42 | "my-analyzer", 43 | tokenizer=tokenizer("split_words", "simple_pattern_split", pattern=":"), 44 | filter=["lowercase", token_filter("no-ifs", "stop", stopwords=["if"])], 45 | ) 46 | 47 | tokens = a.simulate("if:this:works", using=client).tokens 48 | 49 | assert len(tokens) == 2 50 | assert ["this", "works"] == [t.token for t in tokens] 51 | 52 | 53 | def test_simulate_builtin(client: Any) -> None: 54 | a = analyzer("my-analyzer", "english") 55 | tokens = a.simulate("fixes running").tokens 56 | 57 | assert ["fix", "run"] == [t.token for t in tokens] 58 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | This code of conduct applies to all spaces provided by the OpenSource project including in code, documentation, issue trackers, mailing lists, chat channels, wikis, blogs, social media and any other communication channels used by the project. 3 | 4 | 5 | **Our open source communities endeavor to:** 6 | 7 | * Be Inclusive: We are committed to being a community where everyone can join and contribute. This means using inclusive and welcoming language. 8 | * Be Welcoming: We are committed to maintaining a safe space for everyone to be able to contribute. 9 | * Be Respectful: We are committed to encouraging differing viewpoints, accepting constructive criticism and work collaboratively towards decisions that help the project grow. Disrespectful and unacceptable behavior will not be tolerated. 10 | * Be Collaborative: We are committed to supporting what is best for our community and users. When we build anything for the benefit of the project, we should document the work we do and communicate to others on how this affects their work. 11 | 12 | 13 | **Our Responsibility. As contributors, members, or bystanders we each individually have the responsibility to behave professionally and respectfully at all times. Disrespectful and unacceptable behaviors include, but are not limited to:** 14 | 15 | * The use of violent threats, abusive, discriminatory, or derogatory language; 16 | * Offensive comments related to gender, gender identity and expression, sexual orientation, disability, mental illness, race, political or religious affiliation; 17 | * Posting of sexually explicit or violent content; 18 | * The use of sexualized language and unwelcome sexual attention or advances; 19 | * Public or private harassment of any kind; 20 | * Publishing private information, such as physical or electronic address, without permission; 21 | * Other conduct which could reasonably be considered inappropriate in a professional setting; 22 | * Advocating for or encouraging any of the above behaviors. 23 | * Enforcement and Reporting Code of Conduct Issues: 24 | 25 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported. [Contact us](mailto:opensource-codeofconduct@amazon.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. -------------------------------------------------------------------------------- /guides/plugins/index_management.md: -------------------------------------------------------------------------------- 1 | - [Index Management Plugin](#index-management-plugin) 2 | - [Create a Policy](#create-a-policy) 3 | - [Get a Policy](#get-a-policy) 4 | - [Delete a Policy](#delete-a-policy) 5 | 6 | ### Index Management Plugin 7 | 8 | You can use the [Index Management Plugin (ISM) API](https://opensearch.org/docs/latest/im-plugin/ism/api) to programmatically automate periodic, administrative operations on indexes by triggering them based on changes in the index age, index size, or number of documents. 9 | 10 | #### Create a Policy 11 | 12 | ```python 13 | policy_name = "test-policy" 14 | 15 | policy_content = { 16 | "policy": { 17 | "description": "hot warm delete workflow", 18 | "default_state": "hot", 19 | "schema_version": 1, 20 | "states": [ 21 | { 22 | "name": "hot", 23 | "actions": [{"rollover": {"min_index_age": "1d"}}], 24 | "transitions": [{"state_name": "warm"}], 25 | }, 26 | { 27 | "name": "warm", 28 | "actions": [{"replica_count": {"number_of_replicas": 5}}], 29 | "transitions": [{"state_name": "delete", "conditions": {"min_index_age": "30d"}}], 30 | }, 31 | { 32 | "name": "delete", 33 | "actions": [ 34 | { 35 | "notification": { 36 | "destination": {"chime": {"url": ""}}, 37 | "message_template": {"source": "The index {{ctx.index}} is being deleted"}, 38 | } 39 | }, 40 | {"delete": {}}, 41 | ], 42 | }, 43 | ], 44 | "ism_template": {"index_patterns": ["log*"], "priority": 100}, 45 | } 46 | } 47 | 48 | response = client.index_managment.put_policy(policy_name, body=policy_content) 49 | print(response) 50 | ``` 51 | 52 | #### Get a Policy 53 | 54 | ```python 55 | policy_name = "test-policy" 56 | 57 | response = client.index_managment.get_policy(policy_name) 58 | print(response) 59 | ``` 60 | 61 | #### Delete a Policy 62 | 63 | ```python 64 | policy_name = "test-policy" 65 | 66 | response = client.index_managment.delete_policy(policy_name) 67 | print(response) 68 | ``` 69 | -------------------------------------------------------------------------------- /guides/json.md: -------------------------------------------------------------------------------- 1 | - [Making Raw JSON REST Requests](#making-raw-json-rest-requests) 2 | - [GET](#get) 3 | - [PUT](#put) 4 | - [POST](#post) 5 | - [DELETE](#delete) 6 | 7 | # Making Raw JSON REST Requests 8 | 9 | The OpenSearch client implements many high-level REST DSLs that invoke OpenSearch APIs. However you may find yourself in a situation that requires you to invoke an API that is not supported by the client. Use `client.http.get`, `head` , `put`, `post`, and `delete` to do so. See [samples/json](../samples/json) for a complete working sample. 10 | 11 | ## GET 12 | 13 | The following example returns the server version information via `GET /`. 14 | 15 | ```python 16 | info = client.get("/") 17 | print(f"Welcome to {info["version"]["distribution"]} {info["version"]["number"]}!") 18 | ``` 19 | 20 | Note that the client will parse the response as JSON when appropriate. 21 | 22 | These methods are also available in the asynchronous client. 23 | 24 | ```python 25 | info = await client.http.get("/") 26 | print(f"Welcome to {info["version"]["distribution"]} {info["version"]["number"]}!") 27 | ``` 28 | 29 | Use `perform_request` in older versions (<= 2.3.x), and `client.http.get` and others in newer ones. 30 | 31 | ```python 32 | info = client.transport.perform_request("GET", "/") 33 | print(f"Welcome to {info["version"]["distribution"]} {info["version"]["number"]}!") 34 | ``` 35 | 36 | ## PUT 37 | 38 | The following example creates an index. 39 | 40 | ```python 41 | index_body = { 42 | "settings": { 43 | "index": { 44 | "number_of_shards": 4 45 | } 46 | } 47 | } 48 | 49 | client.http.put("/movies", body=index_body) 50 | ``` 51 | 52 | Note that the client will raise errors automatically. For example, if the index already exists, an `opensearchpy.exceptions.RequestError: RequestError(400, "resource_already_exists_exception",` will be thrown. 53 | 54 | ## POST 55 | 56 | The following example searches for a document. 57 | 58 | ```python 59 | q = "miller" 60 | 61 | query = { 62 | "size": 5, 63 | "query": { 64 | "multi_match": { 65 | "query": q, 66 | "fields": ["title^2", "director"] 67 | } 68 | } 69 | } 70 | 71 | client.http.post("/movies/_search", body = query) 72 | ``` 73 | 74 | ## DELETE 75 | 76 | The following example deletes an index. 77 | 78 | ```python 79 | client.http.delete("/movies") 80 | ``` 81 | -------------------------------------------------------------------------------- /test_opensearchpy/test_client/test_cluster.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | from test_opensearchpy.test_cases import OpenSearchTestCase 29 | 30 | 31 | class TestCluster(OpenSearchTestCase): 32 | def test_stats_without_node_id(self) -> None: 33 | self.client.cluster.stats() 34 | self.assert_url_called("GET", "/_cluster/stats") 35 | 36 | def test_stats_with_node_id(self) -> None: 37 | self.client.cluster.stats(node_id="node-1") 38 | self.assert_url_called("GET", "/_cluster/stats/nodes/node-1") 39 | 40 | self.client.cluster.stats(node_id="node-2") 41 | self.assert_url_called("GET", "/_cluster/stats/nodes/node-2") 42 | 43 | def test_state_with_index_without_metric_defaults_to_all(self) -> None: 44 | self.client.cluster.state() 45 | self.assert_url_called("GET", "/_cluster/state") 46 | 47 | self.client.cluster.state(metric="cluster_name") 48 | self.assert_url_called("GET", "/_cluster/state/cluster_name") 49 | 50 | self.client.cluster.state(index="index-1") 51 | self.assert_url_called("GET", "/_cluster/state/_all/index-1") 52 | 53 | self.client.cluster.state(index="index-1", metric="cluster_name") 54 | self.assert_url_called("GET", "/_cluster/state/cluster_name/index-1") 55 | -------------------------------------------------------------------------------- /samples/hello/hello.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # The OpenSearch Contributors require contributions made to 6 | # this file be licensed under the Apache-2.0 license or a 7 | # compatible open source license. 8 | # 9 | # Modifications Copyright OpenSearch Contributors. See 10 | # GitHub history for details. 11 | 12 | 13 | import os 14 | 15 | from opensearchpy import OpenSearch 16 | 17 | # connect to OpenSearch 18 | 19 | 20 | def main() -> None: 21 | """ 22 | An example showing how to create a synchronous connection to 23 | OpenSearch, create an index, index a document and search to 24 | return the document. 25 | """ 26 | host = "localhost" 27 | port = 9200 28 | auth = ( 29 | "admin", 30 | os.getenv("OPENSEARCH_PASSWORD", "admin"), 31 | ) # For testing only. Don't store credentials in code. 32 | 33 | client = OpenSearch( 34 | hosts=[{"host": host, "port": port}], 35 | http_auth=auth, 36 | use_ssl=True, 37 | verify_certs=False, 38 | ssl_show_warn=False, 39 | ) 40 | 41 | info = client.info() 42 | print(f"Welcome to {info['version']['distribution']} {info['version']['number']}!") 43 | 44 | # create an index 45 | 46 | index_name = "test-index" 47 | 48 | index_body = {"settings": {"index": {"number_of_shards": 4}}} 49 | 50 | response = client.indices.create(index_name, body=index_body) 51 | 52 | print(response) 53 | 54 | # add a document to the index 55 | 56 | document = {"title": "Moneyball", "director": "Bennett Miller", "year": "2011"} 57 | 58 | doc_id = "1" 59 | 60 | response = client.index(index=index_name, body=document, id=doc_id, refresh=True) 61 | 62 | print(response) 63 | 64 | # search for a document 65 | 66 | user_query = "miller" 67 | 68 | query = { 69 | "size": 5, 70 | "query": { 71 | "multi_match": {"query": user_query, "fields": ["title^2", "director"]} 72 | }, 73 | } 74 | 75 | response = client.search(body=query, index=index_name) 76 | 77 | print(response) 78 | 79 | # delete the document 80 | 81 | response = client.delete(index=index_name, id=doc_id) 82 | 83 | print(response) 84 | 85 | # delete the index 86 | 87 | response = client.indices.delete(index=index_name) 88 | 89 | print(response) 90 | 91 | 92 | if __name__ == "__main__": 93 | main() 94 | -------------------------------------------------------------------------------- /test_opensearchpy/test_async/test_server/conftest.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | import asyncio 29 | from typing import Any 30 | 31 | import pytest 32 | from _pytest.mark.structures import MarkDecorator 33 | 34 | import opensearchpy 35 | from opensearchpy.helpers.test import OPENSEARCH_URL 36 | 37 | from ...utils import wipe_cluster 38 | 39 | pytestmark: MarkDecorator = pytest.mark.asyncio 40 | 41 | 42 | @pytest.fixture(scope="function") # type: ignore 43 | async def async_client() -> Any: 44 | client = None 45 | try: 46 | if not hasattr(opensearchpy, "AsyncOpenSearch"): 47 | pytest.skip("test requires 'AsyncOpenSearch'") 48 | 49 | kw = {"timeout": 3} 50 | client = opensearchpy.AsyncOpenSearch(OPENSEARCH_URL, **kw) # type: ignore 51 | 52 | # wait for yellow status 53 | for _ in range(100): 54 | try: 55 | await client.cluster.health(wait_for_status="yellow") 56 | break 57 | except ConnectionError: 58 | await asyncio.sleep(0.1) 59 | else: 60 | # timeout 61 | pytest.skip("OpenSearch failed to start.") 62 | 63 | yield client 64 | 65 | finally: 66 | if client: 67 | wipe_cluster(client) 68 | await client.close() 69 | -------------------------------------------------------------------------------- /test_opensearchpy/test_client/test_plugins/test_index_management.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | from test_opensearchpy.test_cases import OpenSearchTestCase 11 | 12 | 13 | class TestIndexManagement(OpenSearchTestCase): 14 | def test_create_policy(self) -> None: 15 | self.client.index_management.put_policy("...") 16 | self.assert_url_called("PUT", "/_plugins/_ism/policies/...") 17 | 18 | def test_update_policy(self) -> None: 19 | self.client.index_management.put_policy( 20 | "...", params={"if_seq_no": 7, "if_primary_term": 1} 21 | ) 22 | self.assertEqual( 23 | [({"if_seq_no": 7, "if_primary_term": 1}, {}, None)], 24 | self.assert_url_called("PUT", "/_plugins/_ism/policies/..."), 25 | ) 26 | 27 | def test_add_policy(self) -> None: 28 | self.client.index_management.add_policy("...") 29 | self.assert_url_called("POST", "/_plugins/_ism/add/...") 30 | 31 | def test_get_policy(self) -> None: 32 | self.client.index_management.get_policy("...") 33 | self.assert_url_called("GET", "/_plugins/_ism/policies/...") 34 | 35 | def test_remove_policy_from_index(self) -> None: 36 | self.client.index_management.remove_policy_from_index("...") 37 | self.assert_url_called("POST", "/_plugins/_ism/remove/...") 38 | 39 | def test_change_policy(self) -> None: 40 | self.client.index_management.change_policy("...") 41 | self.assert_url_called("POST", "/_plugins/_ism/change_policy/...") 42 | 43 | def test_retry(self) -> None: 44 | self.client.index_management.retry("...") 45 | self.assert_url_called("POST", "/_plugins/_ism/retry/...") 46 | 47 | def test_explain_index(self) -> None: 48 | self.client.index_management.explain_index("...", show_policy=True) 49 | self.assertEqual( 50 | [({"show_policy": b"true"}, {}, None)], 51 | self.assert_url_called("GET", "/_plugins/_ism/explain/..."), 52 | ) 53 | 54 | def test_delete_policy(self) -> None: 55 | self.client.index_management.delete_policy("...") 56 | self.assert_url_called("DELETE", "/_plugins/_ism/policies/...") 57 | -------------------------------------------------------------------------------- /opensearchpy/connection/pooling.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | from typing import Any 29 | 30 | from .base import Connection 31 | 32 | try: 33 | import queue 34 | except ImportError: 35 | import Queue as queue # type: ignore 36 | 37 | 38 | class PoolingConnection(Connection): 39 | _free_connections: queue.Queue[Connection] 40 | 41 | """ 42 | Base connection class for connections that use libraries without thread 43 | safety and no capacity for connection pooling. To use this just implement a 44 | ``_make_connection`` method that constructs a new connection and returns 45 | it. 46 | """ 47 | 48 | def __init__(self, *args: Any, **kwargs: Any) -> None: 49 | self._free_connections = queue.Queue() 50 | super().__init__(*args, **kwargs) 51 | 52 | def _make_connection(self) -> Connection: 53 | raise NotImplementedError 54 | 55 | def _get_connection(self) -> Connection: 56 | try: 57 | return self._free_connections.get_nowait() 58 | except queue.Empty: 59 | return self._make_connection() 60 | 61 | def _release_connection(self, con: Connection) -> None: 62 | self._free_connections.put(con) 63 | 64 | def close(self) -> None: 65 | """ 66 | Explicitly close connection 67 | """ 68 | pass 69 | -------------------------------------------------------------------------------- /opensearchpy/helpers/response/hit.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | from typing import Any 28 | 29 | from ..utils import AttrDict, HitMeta 30 | 31 | 32 | class Hit(AttrDict): 33 | def __init__(self, document: Any) -> None: 34 | data = {} 35 | if "_source" in document: 36 | data = document["_source"] 37 | if "fields" in document: 38 | data.update(document["fields"]) 39 | 40 | super().__init__(data) 41 | # assign meta as attribute and not as key in self._d_ 42 | super(AttrDict, self).__setattr__("meta", HitMeta(document)) 43 | 44 | def __getstate__(self) -> Any: 45 | # add self.meta since it is not in self.__dict__ 46 | return super().__getstate__() + (self.meta,) 47 | 48 | def __setstate__(self, state: Any) -> None: 49 | super(AttrDict, self).__setattr__("meta", state[-1]) 50 | super().__setstate__(state[:-1]) 51 | 52 | def __dir__(self) -> Any: 53 | # be sure to expose meta in dir(self) 54 | return super().__dir__() + ["meta"] 55 | 56 | def __repr__(self) -> str: 57 | return "".format( 58 | "/".join( 59 | getattr(self.meta, key) for key in ("index", "id") if key in self.meta 60 | ), 61 | super().__repr__(), 62 | ) 63 | 64 | 65 | __all__ = ["Hit", "HitMeta"] 66 | -------------------------------------------------------------------------------- /test_opensearchpy/test_client/test_indices.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | from test_opensearchpy.test_cases import OpenSearchTestCase 29 | 30 | 31 | class TestIndices(OpenSearchTestCase): 32 | def test_create_one_index(self) -> None: 33 | self.client.indices.create(index="test-index") 34 | self.assert_url_called("PUT", "/test-index") 35 | 36 | def test_delete_multiple_indices(self) -> None: 37 | self.client.indices.delete(index=["test-index", "second.index", "third/index"]) 38 | self.assert_url_called("DELETE", "/test-index,second.index,third%2Findex") 39 | 40 | def test_exists_index(self) -> None: 41 | self.client.indices.exists(index="second.index,third/index") 42 | self.assert_url_called("HEAD", "/second.index,third%2Findex") 43 | 44 | def test_passing_empty_value_for_required_param_raises_exception(self) -> None: 45 | self.assertRaises(ValueError, self.client.indices.exists, index=None) 46 | self.assertRaises(ValueError, self.client.indices.exists, index=[]) 47 | self.assertRaises(ValueError, self.client.indices.exists, index="") 48 | 49 | def test_create_alias(self) -> None: 50 | self.client.indices.create(index="test-index") 51 | self.client.indices.put_alias(index="test-index", name="test-alias") 52 | self.assert_url_called("PUT", "/test-index/_alias/test-alias") 53 | -------------------------------------------------------------------------------- /test_opensearchpy/test_async/test_server/test_helpers/test_update_by_query.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | from typing import Any 11 | 12 | import pytest 13 | from _pytest.mark.structures import MarkDecorator 14 | 15 | from opensearchpy._async.helpers.update_by_query import AsyncUpdateByQuery 16 | from opensearchpy.helpers.search import Q 17 | 18 | pytestmark: MarkDecorator = pytest.mark.asyncio 19 | 20 | 21 | async def test_update_by_query_no_script( 22 | write_client: Any, setup_update_by_query_tests: Any 23 | ) -> None: 24 | index = setup_update_by_query_tests 25 | 26 | ubq = ( 27 | AsyncUpdateByQuery(using=write_client) 28 | .index(index) 29 | .filter(~Q("exists", field="is_public")) 30 | ) 31 | response = await ubq.execute() 32 | 33 | assert response.total == 52 34 | assert response["took"] > 0 35 | assert not response.timed_out 36 | assert response.updated == 52 37 | assert response.deleted == 0 38 | assert response.took > 0 39 | assert response.success() 40 | 41 | 42 | async def test_update_by_query_with_script( 43 | write_client: Any, setup_update_by_query_tests: Any 44 | ) -> None: 45 | index = setup_update_by_query_tests 46 | 47 | ubq = ( 48 | AsyncUpdateByQuery(using=write_client) 49 | .index(index) 50 | .filter(~Q("exists", field="parent_shas")) 51 | .script(source="ctx._source.is_public = false") 52 | ) 53 | ubq = ubq.params(conflicts="proceed") 54 | 55 | response = await ubq.execute() 56 | assert response.total == 2 57 | assert response.updated == 2 58 | assert response.version_conflicts == 0 59 | 60 | 61 | async def test_delete_by_query_with_script( 62 | write_client: Any, setup_update_by_query_tests: Any 63 | ) -> None: 64 | index = setup_update_by_query_tests 65 | 66 | ubq = ( 67 | AsyncUpdateByQuery(using=write_client) 68 | .index(index) 69 | .filter(Q("match", parent_shas="1dd19210b5be92b960f7db6f66ae526288edccc3")) 70 | .script(source='ctx.op = "delete"') 71 | ) 72 | ubq = ubq.params(conflicts="proceed") 73 | 74 | response = await ubq.execute() 75 | 76 | assert response.total == 1 77 | assert response.deleted == 1 78 | assert response.success() 79 | -------------------------------------------------------------------------------- /guides/ssl.md: -------------------------------------------------------------------------------- 1 | - [SSL](#ssl) 2 | - [Establishing a Secure Connection](#establishing-a-secure-connection) 3 | - [Verifying SSL against a Different Host](#verifying-ssl-against-a-different-host) 4 | 5 | # SSL 6 | 7 | ## Establishing a Secure Connection 8 | 9 | ```python 10 | from opensearchpy import OpenSearch 11 | 12 | host = 'localhost' 13 | port = 9200 14 | auth = ('admin', 'admin') # For testing only. Don't store credentials in code. 15 | 16 | # Provide a CA bundle if you use intermediate CAs with your root CA. 17 | # If this is not given, the CA bundle is discovered from the first available 18 | # following options: 19 | # - OpenSSL environment variables SSL_CERT_FILE and SSL_CERT_DIR 20 | # - certifi bundle (https://pypi.org/project/certifi/) 21 | # - default behavior of the connection backend (most likely system certs) 22 | ca_certs_path = '/full/path/to/root-ca.pem' 23 | 24 | # Optional client certificates if you don't want to use HTTP basic authentication. 25 | # client_cert_path = '/full/path/to/client.pem' 26 | # client_key_path = '/full/path/to/client-key.pem' 27 | 28 | # Create the client with SSL/TLS enabled 29 | client = OpenSearch( 30 | hosts = [{'host': host, 'port': port}], 31 | http_compress = True, # enables gzip compression for request bodies 32 | http_auth = auth, 33 | # client_cert = client_cert_path, 34 | # client_key = client_key_path, 35 | use_ssl = True, 36 | verify_certs = True, 37 | ssl_assert_hostname = False, # Hostname verification is disabled here, but by default, it will remain enabled. 38 | ssl_show_warn = False, 39 | ca_certs = ca_certs_path 40 | ) 41 | ``` 42 | When `ssl_assert_hostname` is set to None, verification is conducted using server hostname, effectively equivalent to not setting ssl_assert_hostname. 43 | 44 | 45 | ## Verifying SSL against a different host 46 | 47 | When the server you’re connecting to presents a different certificate than the hostname, you can use ssl_assert_hostname: 48 | 49 | ```python 50 | from opensearchpy import OpenSearch 51 | 52 | host = 'localhost' 53 | port = 9200 54 | auth = ('admin', 'admin') 55 | ca_certs_path = '/full/path/to/root-ca.pem' 56 | 57 | client = OpenSearch( 58 | hosts = [{'host': host, 'port': port}], 59 | http_compress = True, 60 | http_auth = auth, 61 | use_ssl = True, 62 | verify_certs = True, 63 | ssl_assert_hostname = "ssl.com", # Indicate the host name to assert against. By default, it is equal to the server hostname. 64 | ssl_show_warn = False, 65 | ca_certs = ca_certs_path 66 | ) 67 | ``` 68 | -------------------------------------------------------------------------------- /test_opensearchpy/test_http_server.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | import json 11 | import threading 12 | from http.server import BaseHTTPRequestHandler, HTTPServer 13 | from typing import Any 14 | 15 | 16 | class TestHTTPRequestHandler(BaseHTTPRequestHandler): 17 | __test__ = False 18 | 19 | def do_GET(self) -> None: # pylint: disable=invalid-name 20 | """ 21 | writes a response out to a file given mocked parameters on this object 22 | """ 23 | headers = self.headers 24 | 25 | if self.path == "/redirect": 26 | new_location = "http://localhost:8090" 27 | self.send_response(302) 28 | self.send_header("Location", new_location) 29 | else: 30 | self.send_response(200) 31 | self.send_header("Content-type", "application/json") 32 | 33 | self.end_headers() 34 | 35 | capitalized_headers = {} 36 | for header, value in headers.items(): 37 | capitalized_header = "-".join([word.title() for word in header.split("-")]) 38 | capitalized_headers.update({capitalized_header: value}) 39 | if "Connection" in capitalized_headers: 40 | capitalized_headers.pop("Connection") 41 | 42 | data = {"method": "GET", "headers": capitalized_headers} 43 | self.wfile.write(json.dumps(data).encode("utf-8")) 44 | 45 | 46 | class TestHTTPServer(HTTPServer): 47 | __test__ = False 48 | _server_thread: Any 49 | 50 | def __init__(self, host: str = "localhost", port: int = 8080) -> None: 51 | super().__init__((host, port), TestHTTPRequestHandler) 52 | self._server_thread = None 53 | 54 | def start(self) -> None: 55 | """ 56 | start the test HTTP server 57 | """ 58 | if self._server_thread is not None: 59 | return 60 | 61 | self._server_thread = threading.Thread(target=self.serve_forever) 62 | self._server_thread.start() 63 | 64 | def stop(self) -> None: 65 | """ 66 | stop the test HTTP server 67 | """ 68 | if self._server_thread is None: 69 | return 70 | self.socket.close() 71 | self.shutdown() 72 | self._server_thread.join() 73 | self._server_thread = None 74 | -------------------------------------------------------------------------------- /samples/knn/knn_basics.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # The OpenSearch Contributors require contributions made to 6 | # this file be licensed under the Apache-2.0 license or a 7 | # compatible open source license. 8 | # 9 | # Modifications Copyright OpenSearch Contributors. See 10 | # GitHub history for details. 11 | 12 | 13 | import os 14 | import random 15 | 16 | from opensearchpy import OpenSearch, helpers 17 | 18 | 19 | def main() -> None: 20 | """ 21 | create, bulk index, and query kNN. then delete the index 22 | """ 23 | # connect to an instance of OpenSearch 24 | 25 | host = os.getenv("HOST", default="localhost") 26 | port = int(os.getenv("PORT", 9200)) 27 | auth = (os.getenv("USERNAME", "admin"), os.getenv("PASSWORD", "admin")) 28 | 29 | client = OpenSearch( 30 | hosts=[{"host": host, "port": port}], 31 | http_auth=auth, 32 | use_ssl=True, 33 | verify_certs=False, 34 | ssl_show_warn=False, 35 | ) 36 | 37 | # check whether an index exists 38 | index_name = "my-index" 39 | dimensions = 5 40 | 41 | if not client.indices.exists(index=index_name): 42 | client.indices.create( 43 | index=index_name, 44 | body={ 45 | "settings": {"index.knn": True}, 46 | "mappings": { 47 | "properties": { 48 | "values": {"type": "knn_vector", "dimension": dimensions}, 49 | } 50 | }, 51 | }, 52 | ) 53 | 54 | # index data 55 | vectors = [] 56 | for i in range(10): 57 | vec = [] 58 | for _ in range(dimensions): 59 | vec.append(round(random.uniform(0, 1), 2)) 60 | 61 | vectors.append( 62 | { 63 | "_index": index_name, 64 | "_id": i, 65 | "values": vec, 66 | } 67 | ) 68 | 69 | # bulk index 70 | helpers.bulk(client, vectors) 71 | 72 | client.indices.refresh(index=index_name) 73 | 74 | # search 75 | vec = [round(random.uniform(0, 1), 2) for _ in range(dimensions)] 76 | print(f"Searching for {vec} ...") 77 | 78 | search_query = {"query": {"knn": {"values": {"vector": vec, "k": 3}}}} 79 | results = client.search(index=index_name, body=search_query) 80 | (print(hit) for hit in results["hits"]["hits"]) 81 | 82 | # delete index 83 | client.indices.delete(index=index_name) 84 | 85 | 86 | if __name__ == "__main__": 87 | main() 88 | -------------------------------------------------------------------------------- /opensearchpy/client/features.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | from typing import Any 29 | 30 | from .utils import NamespacedClient, query_params 31 | 32 | 33 | class FeaturesClient(NamespacedClient): 34 | @query_params("master_timeout", "cluster_manager_timeout") 35 | def get_features(self, params: Any = None, headers: Any = None) -> Any: 36 | """ 37 | Gets a list of features which can be included in snapshots using the 38 | feature_states field when creating a snapshot 39 | 40 | 41 | :arg master_timeout (Deprecated: use cluster_manager_timeout): Explicit operation timeout for connection 42 | to master node 43 | :arg cluster_manager_timeout: Explicit operation timeout for connection 44 | to cluster_manager node 45 | """ 46 | return self.transport.perform_request( 47 | "GET", "/_features", params=params, headers=headers 48 | ) 49 | 50 | @query_params() 51 | def reset_features(self, params: Any = None, headers: Any = None) -> Any: 52 | """ 53 | Resets the internal state of features, usually by deleting system indices 54 | 55 | 56 | .. warning:: 57 | 58 | This API is **experimental** so may include breaking changes 59 | or be removed in a future version 60 | """ 61 | return self.transport.perform_request( 62 | "POST", "/_features/_reset", params=params, headers=headers 63 | ) 64 | -------------------------------------------------------------------------------- /opensearchpy/_async/client/features.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | from typing import Any 29 | 30 | from .utils import NamespacedClient, query_params 31 | 32 | 33 | class FeaturesClient(NamespacedClient): 34 | @query_params("master_timeout", "cluster_manager_timeout") 35 | async def get_features(self, params: Any = None, headers: Any = None) -> Any: 36 | """ 37 | Gets a list of features which can be included in snapshots using the 38 | feature_states field when creating a snapshot 39 | 40 | 41 | :arg master_timeout (Deprecated: use cluster_manager_timeout): Explicit operation timeout for connection 42 | to master node 43 | :arg cluster_manager_timeout: Explicit operation timeout for connection 44 | to cluster_manager node 45 | """ 46 | return await self.transport.perform_request( 47 | "GET", "/_features", params=params, headers=headers 48 | ) 49 | 50 | @query_params() 51 | async def reset_features(self, params: Any = None, headers: Any = None) -> Any: 52 | """ 53 | Resets the internal state of features, usually by deleting system indices 54 | 55 | 56 | .. warning:: 57 | 58 | This API is **experimental** so may include breaking changes 59 | or be removed in a future version 60 | """ 61 | return await self.transport.perform_request( 62 | "POST", "/_features/_reset", params=params, headers=headers 63 | ) 64 | -------------------------------------------------------------------------------- /opensearchpy/compat.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | # 10 | # Licensed to Elasticsearch B.V. under one or more contributor 11 | # license agreements. See the NOTICE file distributed with 12 | # this work for additional information regarding copyright 13 | # ownership. Elasticsearch B.V. licenses this file to you under 14 | # the Apache License, Version 2.0 (the "License"); you may 15 | # not use this file except in compliance with the License. 16 | # You may obtain a copy of the License at 17 | # 18 | # http://www.apache.org/licenses/LICENSE-2.0 19 | # 20 | # Unless required by applicable law or agreed to in writing, 21 | # software distributed under the License is distributed on an 22 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 | # KIND, either express or implied. See the License for the 24 | # specific language governing permissions and limitations 25 | # under the License. 26 | 27 | 28 | from collections.abc import Mapping 29 | from queue import Queue 30 | from typing import Tuple, Type, Union 31 | from urllib.parse import quote, quote_plus, unquote, urlencode, urlparse 32 | 33 | string_types = str, bytes 34 | map = map # pylint: disable=invalid-name 35 | 36 | 37 | def to_str(x: Union[str, bytes], encoding: str = "ascii") -> str: 38 | """ 39 | returns x as a string encoded in "encoding" if it is not already a string 40 | :param x: the value to convert to a str 41 | :param encoding: the encoding to convert to - see https://docs.python.org/3/library/codecs.html#standard-encodings 42 | :return: an encoded str 43 | """ 44 | if not isinstance(x, str): 45 | return x.decode(encoding) 46 | return x 47 | 48 | 49 | def to_bytes(x: Union[str, bytes], encoding: str = "ascii") -> bytes: 50 | if not isinstance(x, bytes): 51 | return x.encode(encoding) 52 | return x 53 | 54 | 55 | try: 56 | reraise_exceptions: Tuple[Type[BaseException], ...] = (RecursionError,) 57 | except NameError: 58 | reraise_exceptions = () 59 | 60 | try: 61 | import asyncio 62 | 63 | reraise_exceptions += (asyncio.CancelledError,) 64 | except (ImportError, AttributeError): 65 | pass 66 | 67 | 68 | __all__ = [ 69 | "string_types", 70 | "reraise_exceptions", 71 | "quote_plus", 72 | "quote", 73 | "urlencode", 74 | "unquote", 75 | "urlparse", 76 | "map", 77 | "Queue", 78 | "Mapping", 79 | ] 80 | -------------------------------------------------------------------------------- /utils/changelog_updater.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | # 3 | # The OpenSearch Contributors require contributions made to 4 | # this file be licensed under the Apache-2.0 license or a 5 | # compatible open source license. 6 | # 7 | # Modifications Copyright OpenSearch Contributors. See 8 | # GitHub history for details. 9 | 10 | import subprocess 11 | 12 | import requests 13 | 14 | 15 | def main() -> None: 16 | """ 17 | Update CHANGELOG.md when API generator produces new code differing from existing. 18 | """ 19 | git_command = "git status" 20 | try: 21 | git_status = subprocess.check_output( 22 | git_command, shell=True, stderr=subprocess.STDOUT 23 | ) 24 | if ( 25 | "Changes to be committed:" in git_status.decode() 26 | or "Changes not staged for commit:" in git_status.decode() 27 | or "Untracked files:" in git_status.decode() 28 | ): 29 | print("Changes detected; updating changelog.") 30 | 31 | base_url = "https://api.github.com/repos/opensearch-project/opensearch-api-specification/commits" 32 | url_with_per_page = base_url + "?per_page=1" 33 | response = requests.get(url_with_per_page) 34 | if response.ok: 35 | commit_info = response.json()[0] 36 | commit_url = commit_info["html_url"] 37 | latest_commit_sha = commit_info.get("sha") 38 | else: 39 | raise Exception( 40 | f"Failed to fetch opensearch-api-specification commit information. Status code: {response.status_code}" 41 | ) 42 | 43 | with open("CHANGELOG.md", "r+", encoding="utf-8") as file: 44 | content = file.read() 45 | if commit_url not in content: 46 | if "### Updated APIs" in content: 47 | file_content = content.replace( 48 | "### Updated APIs", 49 | f"### Updated APIs\n- Updated opensearch-py APIs to reflect [opensearch-api-specification@{latest_commit_sha[:7]}]({commit_url})", 50 | 1, 51 | ) 52 | file.seek(0) 53 | file.write(file_content) 54 | file.truncate() 55 | else: 56 | raise Exception( 57 | "'Updated APIs' section is not present in CHANGELOG.md" 58 | ) 59 | else: 60 | print("No changes detected") 61 | 62 | except subprocess.CalledProcessError as e: 63 | print(f"Error occurred while checking Git status: {e}") 64 | 65 | 66 | if __name__ == "__main__": 67 | main() 68 | --------------------------------------------------------------------------------