From 0a67529f75cb7598b6d5acc1de628427d2559170 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 14:31:14 +0400 Subject: [PATCH 01/36] Allow running .buildkite scripts on macOS (#3087) (#3101) realpath -s is a GNU extension, but we don't need to care about symlinks here. (cherry picked from commit dd3957903b96d32a3ecbaa506892bd3c4b8fc9b8) Co-authored-by: Quentin Pradet --- .buildkite/functions/imports.sh | 2 +- .buildkite/run-elasticsearch.sh | 2 +- .buildkite/run-tests | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.buildkite/functions/imports.sh b/.buildkite/functions/imports.sh index e732ebba0..ad7a20269 100755 --- a/.buildkite/functions/imports.sh +++ b/.buildkite/functions/imports.sh @@ -43,7 +43,7 @@ if [[ -z $es_node_name ]]; then fi - export script_path=$(dirname $(realpath -s $0)) + export script_path=$(dirname $(realpath $0)) source $script_path/functions/cleanup.sh source $script_path/functions/wait-for-container.sh trap "cleanup_trap ${network_name}" EXIT diff --git a/.buildkite/run-elasticsearch.sh b/.buildkite/run-elasticsearch.sh index 2f73ea8d1..2dda5b4af 100755 --- a/.buildkite/run-elasticsearch.sh +++ b/.buildkite/run-elasticsearch.sh @@ -21,7 +21,7 @@ # - Moved ELASTIC_PASSWORD and xpack.security.enabled to the base arguments for "Security On by default" # - Use https only when TEST_SUITE is "platinum", when "free" use http -script_path=$(dirname $(realpath -s $0)) +script_path=$(dirname $(realpath $0)) source $script_path/functions/imports.sh set -euo pipefail diff --git a/.buildkite/run-tests b/.buildkite/run-tests index 5d6b38039..90a95a209 100755 --- a/.buildkite/run-tests +++ b/.buildkite/run-tests @@ -10,7 +10,7 @@ export TEST_SUITE="${TEST_SUITE:=platinum}" export PYTHON_VERSION="${PYTHON_VERSION:=3.13}" export PYTHON_CONNECTION_CLASS="${PYTHON_CONNECTION_CLASS:=urllib3}" -script_path=$(dirname $(realpath -s $0)) +script_path=$(dirname $(realpath $0)) source $script_path/functions/imports.sh set -euo pipefail From 9baf738e5be085bbb7eb1b7ad476bdba408179f1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 14:34:46 +0100 Subject: [PATCH 02/36] Update the compatiblity mode section of the docs (#3098) (#3106) * Update the compatiblity mode section of the docs * merge with compatiblity section in index.md (cherry picked from commit fcdbbc09584f11400f88c829ed14b14cc6a60281) Co-authored-by: Miguel Grinberg --- docs/reference/connecting.md | 7 ------- docs/reference/index.md | 8 +++++++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/reference/connecting.md b/docs/reference/connecting.md index 27f7fecbc..56b3aaee7 100644 --- a/docs/reference/connecting.md +++ b/docs/reference/connecting.md @@ -277,13 +277,6 @@ client = Elasticsearch( ``` -## Enabling the Compatibility Mode [compatibility-mode] - -The {{es}} server version 8.0 is introducing a new compatibility mode that allows you a smoother upgrade experience from 7 to 8. In a nutshell, you can use the latest 7.x Python {{es}} {{es}} client with an 8.x {{es}} server, giving more room to coordinate the upgrade of your codebase to the next major version. - -If you want to leverage this functionality, please make sure that you are using the latest 7.x Python {{es}} client and set the environment variable `ELASTIC_CLIENT_APIVERSIONING` to `true`. The client is handling the rest internally. For every 8.0 and beyond Python {{es}} client, you’re all set! The compatibility mode is enabled by default. - - ## Using the Client in a Function-as-a-Service Environment [connecting-faas] This section illustrates the best practices for leveraging the {{es}} client in a Function-as-a-Service (FaaS) environment. diff --git a/docs/reference/index.md b/docs/reference/index.md index 6046d7801..03143bbb2 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -69,8 +69,14 @@ Compatibility does not imply full feature parity. New {{es}} features are suppor {{es}} language clients are also _backward compatible_ across minor versions — with default distributions and without guarantees. +### Major version upgrades + :::{tip} To upgrade to a new major version, first upgrade {{es}}, then upgrade the Python {{es}} client. ::: -If you need to work with multiple client versions, note that older versions are also released as `elasticsearch7` and `elasticsearch8`. +Since version 8.0, the {{es}} server supports a compatibility mode that allows smoother upgrade experiences. In a nutshell, this makes it possible to upgrade the {{es}} server to the next major version, while continuing to use the same client. This gives more room to coordinate the upgrade of your codebase to the next major version. + +For example, to upgrade a system that uses {{es}} 8.x you can upgrade the {{es}} server to 9.x first, and the 8.x Python {{es}} client will continue to work (aside from any breaking changes, which should be listed in the server release notes). You can continue using the 8.x client during the server migration, and only upgrade it once the server migration is complete. The process is described in detail in the [REST API compatibility workflow](https://www.elastic.co/docs/reference/elasticsearch/rest-apis/compatibility#_rest_api_compatibility_workflow) section of the {{es}} documentation. + +If you need to work with multiple client versions, note that older versions are also released with the `elasticsearch8` and `elasticsearch9` package names so that they can be installed together. From 61421d1e1593002d7f356743bb725819f63875ba Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 16:30:46 +0100 Subject: [PATCH 03/36] Add TS, FUSE and INLINE STATS ES|QL commands and recently added functions (#3096) (#3110) * Various fixes in the ES|QL query builder docstrings * add TS, FUSE and INLINE STATS commands * add new ES|QL functions (cherry picked from commit e62eaf231820c30eb02d1e5ff52e6cfc7fcb1e90) Co-authored-by: Miguel Grinberg --- elasticsearch/esql/esql.py | 261 +++++++++++++++++++++++++++----- elasticsearch/esql/functions.py | 88 +++++++++++ test_elasticsearch/test_esql.py | 164 ++++++++++++++++++++ 3 files changed, 475 insertions(+), 38 deletions(-) diff --git a/elasticsearch/esql/esql.py b/elasticsearch/esql/esql.py index 6643ddc67..501148e39 100644 --- a/elasticsearch/esql/esql.py +++ b/elasticsearch/esql/esql.py @@ -18,7 +18,7 @@ import json import re from abc import ABC, abstractmethod -from typing import Any, Dict, Optional, Tuple, Type, Union +from typing import Any, Dict, List, Optional, Tuple, Type, Union from ..dsl.document_base import DocumentBase, InstrumentedExpression, InstrumentedField @@ -78,6 +78,22 @@ def show(item: str) -> "Show": """ return Show(item) + @staticmethod + def ts(*indices: IndexType) -> "TS": + """The ``TS`` source command is similar to ``FROM``, but for time series indices. + + :param indices: A list of indices, data streams or aliases. Supports wildcards and date math. + + Examples:: + + query = ( + ESQL.ts("metrics") + .where("@timestamp >= now() - 1 day") + .stats("SUM(AVG_OVER_TIME(memory_usage)").by("host", "TBUCKET(1 hour)") + ) + """ + return TS(*indices) + @staticmethod def branch() -> "Branch": """This method can only be used inside a ``FORK`` command to create each branch. @@ -284,14 +300,14 @@ def eval(self, *columns: ExpressionType, **named_columns: ExpressionType) -> "Ev def fork( self, - fork1: "ESQLBase", - fork2: Optional["ESQLBase"] = None, - fork3: Optional["ESQLBase"] = None, - fork4: Optional["ESQLBase"] = None, - fork5: Optional["ESQLBase"] = None, - fork6: Optional["ESQLBase"] = None, - fork7: Optional["ESQLBase"] = None, - fork8: Optional["ESQLBase"] = None, + fork1: "Branch", + fork2: Optional["Branch"] = None, + fork3: Optional["Branch"] = None, + fork4: Optional["Branch"] = None, + fork5: Optional["Branch"] = None, + fork6: Optional["Branch"] = None, + fork7: Optional["Branch"] = None, + fork8: Optional["Branch"] = None, ) -> "Fork": """The ``FORK`` processing command creates multiple execution branches to operate on the same input data and combines the results in a single output table. @@ -314,6 +330,51 @@ def fork( raise ValueError("a query can only have one fork") return Fork(self, fork1, fork2, fork3, fork4, fork5, fork6, fork7, fork8) + def fuse(self, method: Optional[str] = None) -> "Fuse": + """The ``FUSE`` processing command merges rows from multiple result sets and assigns + new relevance scores. + + :param method: Defaults to ``RRF``. Can be one of ``RRF`` (for Reciprocal Rank Fusion) + or ``LINEAR`` (for linear combination of scores). Designates which + method to use to assign new relevance scores. + + Examples:: + + query1 = ( + ESQL.from_("books").metadata("_id", "_index", "_score") + .fork( + ESQL.branch().where('title:"Shakespeare"').sort("_score DESC"), + ESQL.branch().where('semantic_title:"Shakespeare"').sort("_score DESC"), + ) + .fuse() + ) + query2 = ( + ESQL.from_("books").metadata("_id", "_index", "_score") + .fork( + ESQL.branch().where('title:"Shakespeare"').sort("_score DESC"), + ESQL.branch().where('semantic_title:"Shakespeare"').sort("_score DESC"), + ) + .fuse("linear") + ) + query3 = ( + ESQL.from_("books").metadata("_id", "_index", "_score") + .fork( + ESQL.branch().where('title:"Shakespeare"').sort("_score DESC"), + ESQL.branch().where('semantic_title:"Shakespeare"').sort("_score DESC"), + ) + .fuse("linear").by("title", "description") + ) + query4 = ( + ESQL.from_("books").metadata("_id", "_index", "_score") + .fork( + ESQL.branch().where('title:"Shakespeare"').sort("_score DESC"), + ESQL.branch().where('semantic_title:"Shakespeare"').sort("_score DESC"), + ) + .fuse("linear").with_(normalizer="minmax") + ) + """ + return Fuse(self, method) + def grok(self, input: FieldType, pattern: str) -> "Grok": """``GROK`` enables you to extract structured data out of a string. @@ -348,6 +409,58 @@ def grok(self, input: FieldType, pattern: str) -> "Grok": """ return Grok(self, input, pattern) + def inline_stats( + self, *expressions: ExpressionType, **named_expressions: ExpressionType + ) -> "Stats": + """The ``INLINE STATS`` processing command groups rows according to a common value + and calculates one or more aggregated values over the grouped rows. + + The command is identical to ``STATS`` except that it preserves all the columns from + the input table. + + :param expressions: A list of expressions, given as positional arguments. + :param named_expressions: A list of expressions, given as keyword arguments. The + argument names are used for the returned aggregated values. + + Note that only one of ``expressions`` and ``named_expressions`` must be provided. + + Examples:: + + query1 = ( + ESQL.from_("employees") + .keep("emp_no", "languages", "salary") + .inline_stats(max_salary=functions.max(E("salary"))).by("languages") + ) + query2 = ( + ESQL.from_("employees") + .keep("emp_no", "languages", "salary") + .inline_stats(max_salary=functions.max(E("salary"))) + ) + query3 = ( + ESQL.from_("employees") + .where("still_hired") + .keep("emp_no", "languages", "salary", "hire_date") + .eval(tenure=functions.date_diff("year", E("hire_date"), "2025-09-18T00:00:00")) + .drop("hire_date") + .inline_stats( + avg_salary=functions.avg(E("salary")), + count=functions.count(E("*")), + ) + .by("languages", "tenure") + ) + query4 = ( + ESQL.from_("employees") + .keep("emp_no", "salary") + .inline_stats( + avg_lt_50=functions.round(functions.avg(E("salary"))).where(E("salary") < 50000), + avg_lt_60=functions.round(functions.avg(E("salary"))).where(E("salary") >= 50000, E("salary") < 60000), + avg_gt_60=functions.round(functions.avg(E("salary"))).where(E("salary") >= 60000), + ) + ) + + """ + return InlineStats(self, *expressions, **named_expressions) + def keep(self, *columns: FieldType) -> "Keep": """The ``KEEP`` processing command enables you to specify what columns are returned and the order in which they are returned. @@ -377,7 +490,7 @@ def limit(self, max_number_of_rows: int) -> "Limit": return Limit(self, max_number_of_rows) def lookup_join(self, lookup_index: IndexType) -> "LookupJoin": - """`LOOKUP JOIN` enables you to add data from another index, AKA a 'lookup' index, + """``LOOKUP JOIN`` enables you to add data from another index, AKA a 'lookup' index, to your ES|QL query results, simplifying data enrichment and analysis workflows. :param lookup_index: The name of the lookup index. This must be a specific index @@ -411,7 +524,7 @@ def lookup_join(self, lookup_index: IndexType) -> "LookupJoin": return LookupJoin(self, lookup_index) def mv_expand(self, column: FieldType) -> "MvExpand": - """The `MV_EXPAND` processing command expands multivalued columns into one row per + """The ``MV_EXPAND`` processing command expands multivalued columns into one row per value, duplicating other columns. :param column: The multivalued column to expand. @@ -449,7 +562,7 @@ def rerank(self, *query: ExpressionType, **named_query: ExpressionType) -> "Rera :param named_query: The query text used to rerank the documents, given as a keyword argument. The argument name is used for the column name. If the query is given as a positional argument, the - results will be stored in a column named `_score`. If the + results will be stored in a column named ``_score``. If the specified column already exists, it will be overwritten with the new results. @@ -540,7 +653,7 @@ def stats( :param named_expressions: A list of expressions, given as keyword arguments. The argument names are used for the returned aggregated values. - Note that only one of `expressions` and `named_expressions` must be provided. + Note that only one of ``expressions`` and ``named_expressions`` must be provided. Examples:: @@ -596,7 +709,7 @@ def stats( def where(self, *expressions: ExpressionType) -> "Where": """The ``WHERE`` processing command produces a table that contains all the rows - from the input table for which the provided condition evaluates to `true`. + from the input table for which the provided condition evaluates to ``true``. :param expressions: A list of boolean expressions, given as positional arguments. These expressions are combined with an ``AND`` logical operator. @@ -629,13 +742,15 @@ class From(ESQLBase): in a single expression. """ + command_name = "FROM" + def __init__(self, *indices: IndexType): super().__init__() self._indices = indices self._metadata_fields: Tuple[FieldType, ...] = tuple() def metadata(self, *fields: FieldType) -> "From": - """Continuation of the ``FROM`` source command. + """Continuation of the ``FROM`` and ``TS`` source commands. :param fields: metadata fields to retrieve, given as positional arguments. """ @@ -644,7 +759,7 @@ def metadata(self, *fields: FieldType) -> "From": def _render_internal(self) -> str: indices = [self._format_index(index) for index in self._indices] - s = f'{self.__class__.__name__.upper()} {", ".join(indices)}' + s = f'{self.command_name} {", ".join(indices)}' if self._metadata_fields: s = ( s @@ -692,6 +807,17 @@ def _render_internal(self) -> str: return f"SHOW {self._format_id(self._item)}" +class TS(From): + """Implementation of the ``TS`` source command. + + This class inherits from :class:`ESQLBase `, + to make it possible to chain all the commands that belong to an ES|QL query + in a single expression. + """ + + command_name = "TS" + + class Branch(ESQLBase): """Implementation of a branch inside a ``FORK`` processing command. @@ -720,21 +846,22 @@ def __init__(self, parent: ESQLBase, value: FieldType): self._pvalue_name: Optional[str] = None def on(self, key: FieldType) -> "ChangePoint": - """Continuation of the `CHANGE_POINT` command. + """Continuation of the ``CHANGE_POINT`` command. :param key: The column with the key to order the values by. If not specified, - `@timestamp` is used. + ``@timestamp`` is used. """ self._key = key return self def as_(self, type_name: str, pvalue_name: str) -> "ChangePoint": - """Continuation of the `CHANGE_POINT` command. + """Continuation of the ``CHANGE_POINT`` command. :param type_name: The name of the output column with the change point type. - If not specified, `type` is used. + If not specified, ``type`` is used. :param pvalue_name: The name of the output column with the p-value that indicates - how extreme the change point is. If not specified, `pvalue` is used. + how extreme the change point is. If not specified, ``pvalue`` + is used. """ self._type_name = type_name self._pvalue_name = pvalue_name @@ -771,10 +898,10 @@ def __init__( self._inference_id: Optional[str] = None def with_(self, inference_id: str) -> "Completion": - """Continuation of the `COMPLETION` command. + """Continuation of the ``COMPLETION`` command. :param inference_id: The ID of the inference endpoint to use for the task. The - inference endpoint must be configured with the completion + inference endpoint must be configured with the ``completion`` task type. """ self._inference_id = inference_id @@ -863,7 +990,7 @@ def on(self, match_field: FieldType) -> "Enrich": :param match_field: The match field. ``ENRICH`` uses its value to look for records in the enrich index. If not specified, the match will be performed on the column with the same name as the - `match_field` defined in the enrich policy. + ``match_field`` defined in the enrich policy. """ self._match_field = match_field return self @@ -953,14 +1080,14 @@ class Fork(ESQLBase): def __init__( self, parent: ESQLBase, - fork1: ESQLBase, - fork2: Optional[ESQLBase] = None, - fork3: Optional[ESQLBase] = None, - fork4: Optional[ESQLBase] = None, - fork5: Optional[ESQLBase] = None, - fork6: Optional[ESQLBase] = None, - fork7: Optional[ESQLBase] = None, - fork8: Optional[ESQLBase] = None, + fork1: "Branch", + fork2: Optional["Branch"] = None, + fork3: Optional["Branch"] = None, + fork4: Optional["Branch"] = None, + fork5: Optional["Branch"] = None, + fork6: Optional["Branch"] = None, + fork7: Optional["Branch"] = None, + fork8: Optional["Branch"] = None, ): super().__init__(parent) self._branches = [fork1, fork2, fork3, fork4, fork5, fork6, fork7, fork8] @@ -977,6 +1104,39 @@ def _render_internal(self) -> str: return f"FORK {cmds}" +class Fuse(ESQLBase): + """Implementation of the ``FUSE`` processing command. + + This class inherits from :class:`ESQLBase `, + to make it possible to chain all the commands that belong to an ES|QL query + in a single expression. + """ + + def __init__(self, parent: ESQLBase, method: Optional[str] = None): + super().__init__(parent) + self.method = method + self.by_columns: List[FieldType] = [] + self.options: Dict[str, Any] = {} + + def by(self, *columns: FieldType) -> "Fuse": + self.by_columns += list(columns) + return self + + def with_(self, **options: Any) -> "Fuse": + self.options = options + return self + + def _render_internal(self) -> str: + method = f" {self.method.upper()}" if self.method else "" + by = ( + " " + " ".join([f"BY {column}" for column in self.by_columns]) + if self.by_columns + else "" + ) + with_ = " WITH " + json.dumps(self.options) if self.options else "" + return f"FUSE{method}{by}{with_}" + + class Grok(ESQLBase): """Implementation of the ``GROK`` processing command. @@ -1040,7 +1200,7 @@ def __init__(self, parent: ESQLBase, lookup_index: IndexType): self._field: Optional[FieldType] = None def on(self, field: FieldType) -> "LookupJoin": - """Continuation of the `LOOKUP_JOIN` command. + """Continuation of the ``LOOKUP JOIN`` command. :param field: The field to join on. This field must exist in both your current query results and in the lookup index. If the field contains multi-valued @@ -1117,14 +1277,19 @@ def __init__( self._inference_id: Optional[str] = None def on(self, *fields: str) -> "Rerank": + """Continuation of the ``RERANK`` command. + + :param fields: One or more fields to use for reranking. These fields should + contain the text that the reranking model will evaluate. + """ self._fields = fields return self def with_(self, inference_id: str) -> "Rerank": - """Continuation of the `COMPLETION` command. + """Continuation of the ``RERANK`` command. :param inference_id: The ID of the inference endpoint to use for the task. The - inference endpoint must be configured with the completion + inference endpoint must be configured with the ``rerank`` task type. """ self._inference_id = inference_id @@ -1195,6 +1360,8 @@ class Stats(ESQLBase): in a single expression. """ + command_name = "STATS" + def __init__( self, parent: ESQLBase, @@ -1210,6 +1377,12 @@ def __init__( self._grouping_expressions: Optional[Tuple[ExpressionType, ...]] = None def by(self, *grouping_expressions: ExpressionType) -> "Stats": + """Continuation of the ``STATS`` and ``INLINE STATS`` commands. + + :param grouping_expressions: Expressions that output the values to group by. + If their names coincide with one of the computed + columns, that column will be ignored. + """ self._grouping_expressions = grouping_expressions return self @@ -1221,13 +1394,25 @@ def _render_internal(self) -> str: ] else: exprs = [f"{self._format_expr(expr)}" for expr in self._expressions] - expression_separator = ",\n " + indent = " " * (len(self.command_name) + 3) + expression_separator = f",\n{indent}" by = ( "" if self._grouping_expressions is None - else f'\n BY {", ".join([f"{self._format_expr(expr)}" for expr in self._grouping_expressions])}' + else f'\n{indent}BY {", ".join([f"{self._format_expr(expr)}" for expr in self._grouping_expressions])}' ) - return f'STATS {expression_separator.join([f"{expr}" for expr in exprs])}{by}' + return f'{self.command_name} {expression_separator.join([f"{expr}" for expr in exprs])}{by}' + + +class InlineStats(Stats): + """Implementation of the ``INLINE STATS`` processing command. + + This class inherits from :class:`ESQLBase `, + to make it possible to chain all the commands that belong to an ES|QL query + in a single expression. + """ + + command_name = "INLINE STATS" class Where(ESQLBase): diff --git a/elasticsearch/esql/functions.py b/elasticsearch/esql/functions.py index 162d7b95e..46cf5c9d9 100644 --- a/elasticsearch/esql/functions.py +++ b/elasticsearch/esql/functions.py @@ -38,6 +38,20 @@ def abs(number: ExpressionType) -> InstrumentedExpression: return InstrumentedExpression(f"ABS({_render(number)})") +def absent(field: ExpressionType) -> InstrumentedExpression: + """Returns true if the input expression yields no non-null values within the + current aggregation context. + + :param field: Expression that outputs values to be checked for absence. + """ + return InstrumentedExpression(f"ABSENT({_render(field)})") + + +def absent_over_time(field: ExpressionType) -> InstrumentedExpression: + """Calculates the absence of a field in the output result over time range.""" + return InstrumentedExpression(f"ABSENT_OVER_TIME({_render(field)})") + + def acos(number: ExpressionType) -> InstrumentedExpression: """Returns the arccosine of `n` as an angle, expressed in radians. @@ -364,6 +378,11 @@ def exp(number: ExpressionType) -> InstrumentedExpression: return InstrumentedExpression(f"EXP({_render(number)})") +def first(value: ExpressionType, sort: ExpressionType) -> InstrumentedExpression: + """Calculates the earliest value of a field.""" + return InstrumentedExpression(f"FIRST({_render(value)}, {_render(sort)})") + + def first_over_time(field: ExpressionType) -> InstrumentedExpression: """The earliest value of a field, where recency determined by the `@timestamp` field. @@ -463,6 +482,11 @@ def kql(query: ExpressionType) -> InstrumentedExpression: return InstrumentedExpression(f"KQL({_render(query)})") +def last(value: ExpressionType, sort: ExpressionType) -> InstrumentedExpression: + """Calculates the latest value of a field.""" + return InstrumentedExpression(f"LAST({_render(value)}, {_render(sort)})") + + def last_over_time(field: ExpressionType) -> InstrumentedExpression: """The latest value of a field, where recency determined by the `@timestamp` field. @@ -697,6 +721,18 @@ def mv_concat(string: ExpressionType, delim: ExpressionType) -> InstrumentedExpr return InstrumentedExpression(f"MV_CONCAT({_render(string)}, {_render(delim)})") +def mv_contains( + superset: ExpressionType, subset: ExpressionType +) -> InstrumentedExpression: + """Checks if all values yielded by the second multivalue expression are present in the + values yielded by the first multivalue expression. Returns a boolean. Null values are + treated as an empty set. + """ + return InstrumentedExpression( + f"MV_CONTAINS({_render(superset)}, {_render(subset)})" + ) + + def mv_count(field: ExpressionType) -> InstrumentedExpression: """Converts a multivalued expression into a single valued column containing a count of the number of values. @@ -894,6 +930,18 @@ def pow(base: ExpressionType, exponent: ExpressionType) -> InstrumentedExpressio return InstrumentedExpression(f"POW({_render(base)}, {_render(exponent)})") +def present(field: ExpressionType) -> InstrumentedExpression: + """Returns true if the input expression yields any non-null values within the current + aggregation context. Otherwise it returns false. + """ + return InstrumentedExpression(f"PRESENT({_render(field)})") + + +def present_over_time(field: ExpressionType) -> InstrumentedExpression: + """Calculates the presence of a field in the output result over time range.""" + return InstrumentedExpression(f"PRESENT_OVER_TIME({_render(field)})") + + def qstr( query: ExpressionType, options: ExpressionType = None ) -> InstrumentedExpression: @@ -1452,6 +1500,11 @@ def sum(number: ExpressionType) -> InstrumentedExpression: return InstrumentedExpression(f"SUM({_render(number)})") +def sum_over_time(field: ExpressionType) -> InstrumentedExpression: + """Calculates the sum over time value of a field.""" + return InstrumentedExpression(f"SUM({_render(field)})") + + def tan(angle: ExpressionType) -> InstrumentedExpression: """Returns the tangent of an angle. @@ -1483,6 +1536,17 @@ def term(field: ExpressionType, query: ExpressionType) -> InstrumentedExpression return InstrumentedExpression(f"TERM({_render(field)}, {_render(query)})") +def text_embedding( + text: ExpressionType, inference_id: ExpressionType +) -> InstrumentedExpression: + """Generates dense vector embeddings from text input using a specified inference endpoint. + Use this function to generate query vectors for KNN searches against your vectorized data + or others dense vector based operations.""" + return InstrumentedExpression( + f"TEXT_EMBEDDING({_render(text)}, {_render(inference_id)})" + ) + + def top( field: ExpressionType, limit: ExpressionType, order: ExpressionType ) -> InstrumentedExpression: @@ -1596,6 +1660,22 @@ def to_double(field: ExpressionType) -> InstrumentedExpression: return InstrumentedExpression(f"TO_DOUBLE({_render(field)})") +def to_geohash(field: ExpressionType) -> InstrumentedExpression: + """Converts an input value to a geohash value. A string will only be successfully + converted if it respects the geohash format, as described for the geohash grid + aggregation. + """ + return InstrumentedExpression(f"TO_GEOHASH({_render(field)})") + + +def to_geohex(field: ExpressionType) -> InstrumentedExpression: + """Converts an input value to a geohex value. A string will only be successfully + converted if it respects the geohex format, as described for the geohex grid + aggregation. + """ + return InstrumentedExpression(f"TO_GEOHEX({_render(field)})") + + def to_geopoint(field: ExpressionType) -> InstrumentedExpression: """Converts an input value to a `geo_point` value. A string will only be successfully converted if it respects the WKT Point format. @@ -1616,6 +1696,14 @@ def to_geoshape(field: ExpressionType) -> InstrumentedExpression: return InstrumentedExpression(f"TO_GEOSHAPE({_render(field)})") +def to_geotile(field: ExpressionType) -> InstrumentedExpression: + """Converts an input value to a geotile value. A string will only be successfully + converted if it respects the geotile format, as described for the geotile grid + aggregation. + """ + return InstrumentedExpression(f"TO_GEOTILE({_render(field)})") + + def to_integer(field: ExpressionType) -> InstrumentedExpression: """Converts an input value to an integer value. If the input parameter is of a date type, its value will be interpreted as milliseconds since the diff --git a/test_elasticsearch/test_esql.py b/test_elasticsearch/test_esql.py index e33f288da..06073da5e 100644 --- a/test_elasticsearch/test_esql.py +++ b/test_elasticsearch/test_esql.py @@ -55,6 +55,22 @@ def test_show(): assert query.render() == "SHOW INFO" +def test_ts(): + query = ( + ESQL.ts("metrics") + .where("@timestamp >= now() - 1 day") + .stats("SUM(AVG_OVER_TIME(memory_usage))") + .by("host", "TBUCKET(1 hour)") + ) + assert ( + query.render() + == """TS metrics +| WHERE @timestamp >= now() - 1 day +| STATS SUM(AVG_OVER_TIME(memory_usage)) + BY host, TBUCKET(1 hour)""" + ) + + def test_change_point(): query = ( ESQL.row(key=list(range(1, 26))) @@ -276,6 +292,78 @@ def test_fork(): ) +def test_fuse(): + query = ( + ESQL.from_("books") + .metadata("_id", "_index", "_score") + .fork( + ESQL.branch().where('title:"Shakespeare"').sort("_score DESC"), + ESQL.branch().where('semantic_title:"Shakespeare"').sort("_score DESC"), + ) + .fuse() + ) + assert ( + query.render() + == """FROM books METADATA _id, _index, _score +| FORK ( WHERE title:"Shakespeare" | SORT _score DESC ) + ( WHERE semantic_title:"Shakespeare" | SORT _score DESC ) +| FUSE""" + ) + + query = ( + ESQL.from_("books") + .metadata("_id", "_index", "_score") + .fork( + ESQL.branch().where('title:"Shakespeare"').sort("_score DESC"), + ESQL.branch().where('semantic_title:"Shakespeare"').sort("_score DESC"), + ) + .fuse("linear") + ) + assert ( + query.render() + == """FROM books METADATA _id, _index, _score +| FORK ( WHERE title:"Shakespeare" | SORT _score DESC ) + ( WHERE semantic_title:"Shakespeare" | SORT _score DESC ) +| FUSE LINEAR""" + ) + + query = ( + ESQL.from_("books") + .metadata("_id", "_index", "_score") + .fork( + ESQL.branch().where('title:"Shakespeare"').sort("_score DESC"), + ESQL.branch().where('semantic_title:"Shakespeare"').sort("_score DESC"), + ) + .fuse("linear") + .by("title", "description") + ) + assert ( + query.render() + == """FROM books METADATA _id, _index, _score +| FORK ( WHERE title:"Shakespeare" | SORT _score DESC ) + ( WHERE semantic_title:"Shakespeare" | SORT _score DESC ) +| FUSE LINEAR BY title BY description""" + ) + + query = ( + ESQL.from_("books") + .metadata("_id", "_index", "_score") + .fork( + ESQL.branch().where('title:"Shakespeare"').sort("_score DESC"), + ESQL.branch().where('semantic_title:"Shakespeare"').sort("_score DESC"), + ) + .fuse("linear") + .with_(normalizer="minmax") + ) + assert ( + query.render() + == """FROM books METADATA _id, _index, _score +| FORK ( WHERE title:"Shakespeare" | SORT _score DESC ) + ( WHERE semantic_title:"Shakespeare" | SORT _score DESC ) +| FUSE LINEAR WITH {"normalizer": "minmax"}""" + ) + + def test_grok(): query = ( ESQL.row(a="2023-01-23T12:15:00.000Z 127.0.0.1 some.email@foo.com 42") @@ -322,6 +410,82 @@ def test_grok(): ) +def test_inline_stats(): + query = ( + ESQL.from_("employees") + .keep("emp_no", "languages", "salary") + .inline_stats(max_salary=functions.max(E("salary"))) + .by("languages") + ) + assert ( + query.render() + == """FROM employees +| KEEP emp_no, languages, salary +| INLINE STATS max_salary = MAX(salary) + BY languages""" + ) + + query = ( + ESQL.from_("employees") + .keep("emp_no", "languages", "salary") + .inline_stats(max_salary=functions.max(E("salary"))) + ) + assert ( + query.render() + == """FROM employees +| KEEP emp_no, languages, salary +| INLINE STATS max_salary = MAX(salary)""" + ) + + query = ( + ESQL.from_("employees") + .where("still_hired") + .keep("emp_no", "languages", "salary", "hire_date") + .eval(tenure=functions.date_diff("year", E("hire_date"), "2025-09-18T00:00:00")) + .drop("hire_date") + .inline_stats( + avg_salary=functions.avg(E("salary")), + count=functions.count(E("*")), + ) + .by("languages", "tenure") + ) + assert ( + query.render() + == """FROM employees +| WHERE still_hired +| KEEP emp_no, languages, salary, hire_date +| EVAL tenure = DATE_DIFF("year", hire_date, "2025-09-18T00:00:00") +| DROP hire_date +| INLINE STATS avg_salary = AVG(salary), + count = COUNT(*) + BY languages, tenure""" + ) + + query = ( + ESQL.from_("employees") + .keep("emp_no", "salary") + .inline_stats( + avg_lt_50=functions.round(functions.avg(E("salary"))).where( + E("salary") < 50000 + ), + avg_lt_60=functions.round(functions.avg(E("salary"))).where( + E("salary") >= 50000, E("salary") < 60000 + ), + avg_gt_60=functions.round(functions.avg(E("salary"))).where( + E("salary") >= 60000 + ), + ) + ) + assert ( + query.render() + == """FROM employees +| KEEP emp_no, salary +| INLINE STATS avg_lt_50 = ROUND(AVG(salary)) WHERE salary < 50000, + avg_lt_60 = ROUND(AVG(salary)) WHERE (salary >= 50000) AND (salary < 60000), + avg_gt_60 = ROUND(AVG(salary)) WHERE salary >= 60000""" + ) + + def test_keep(): query = ESQL.from_("employees").keep("emp_no", "first_name", "last_name", "height") assert ( From 33bcc7b057c8df0ad94c33b63f0adbf8fbacdcb9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 17:00:49 +0000 Subject: [PATCH 04/36] Bump checkout action from v4 to v5 (#3094) (#3108) (cherry picked from commit a11aab5f5a97874e7fc917607e582cda86cdf32f) Co-authored-by: Riccardo Solazzi <129967922+TheZalRevolt@users.noreply.github.com> Co-authored-by: riccardo solazzi Co-authored-by: Miguel Grinberg --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0bc43d985..b335bfac0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up Python 3.x uses: actions/setup-python@v5 with: @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up Python 3.x uses: actions/setup-python@v5 with: @@ -47,7 +47,7 @@ jobs: continue-on-error: false steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set Up Python - ${{ matrix.python-version }} uses: actions/setup-python@v5 with: From 14adafa5f9adc54760909404d3a2e5aea232e485 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 12:53:03 +0100 Subject: [PATCH 05/36] Add 3.14 to CI builds (#3103) (#3112) * Add 3.14 to CI builds * Support Python 3.14 __annotate_func__ * Temporarily remove pyarrow * Update pyproject.toml * cleanup annotationlib use --------- (cherry picked from commit 1f43ab95f1a863f0f58b5818f272911d89060d3f) Co-authored-by: Miguel Grinberg Co-authored-by: Quentin Pradet --- .buildkite/Dockerfile | 2 +- .buildkite/pipeline.yml | 5 +++-- .buildkite/run-tests | 2 +- .github/workflows/ci.yml | 2 +- elasticsearch/dsl/document_base.py | 15 +++++++++++++++ noxfile.py | 4 ++-- pyproject.toml | 2 +- 7 files changed, 24 insertions(+), 8 deletions(-) diff --git a/.buildkite/Dockerfile b/.buildkite/Dockerfile index a68ad997d..2c6bfbe6c 100644 --- a/.buildkite/Dockerfile +++ b/.buildkite/Dockerfile @@ -1,4 +1,4 @@ -ARG PYTHON_VERSION=3.13 +ARG PYTHON_VERSION=3.14 FROM python:${PYTHON_VERSION} # Default UID/GID to 1000 diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index ac7c6b60a..729336bac 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -16,6 +16,7 @@ steps: - "3.11" - "3.12" - "3.13" + - "3.14" connection: - "urllib3" - "requests" @@ -23,11 +24,11 @@ steps: - "test" adjustments: - with: - python: "3.9" + python: "3.10" connection: "urllib3" nox_session: "test_otel" - with: - python: "3.13" + python: "3.14" connection: "urllib3" nox_session: "test_otel" command: ./.buildkite/run-tests diff --git a/.buildkite/run-tests b/.buildkite/run-tests index 90a95a209..8d0eb7ffd 100755 --- a/.buildkite/run-tests +++ b/.buildkite/run-tests @@ -7,7 +7,7 @@ # Default environment variables export STACK_VERSION="${STACK_VERSION:=8.0.0-SNAPSHOT}" export TEST_SUITE="${TEST_SUITE:=platinum}" -export PYTHON_VERSION="${PYTHON_VERSION:=3.13}" +export PYTHON_VERSION="${PYTHON_VERSION:=3.14}" export PYTHON_CONNECTION_CLASS="${PYTHON_CONNECTION_CLASS:=urllib3}" script_path=$(dirname $(realpath $0)) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b335bfac0..20663c601 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] nox-session: [""] runs-on: ["ubuntu-latest"] diff --git a/elasticsearch/dsl/document_base.py b/elasticsearch/dsl/document_base.py index 4df900a39..72f0364a4 100644 --- a/elasticsearch/dsl/document_base.py +++ b/elasticsearch/dsl/document_base.py @@ -34,6 +34,11 @@ overload, ) +try: + import annotationlib +except ImportError: + annotationlib = None + try: from types import UnionType except ImportError: @@ -332,6 +337,16 @@ def __init__(self, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]): # # ignore attributes # field10: ClassVar[string] = "a regular class variable" annotations = attrs.get("__annotations__", {}) + if not annotations and annotationlib: + # Python 3.14+ uses annotationlib + annotate = annotationlib.get_annotate_from_class_namespace(attrs) + if annotate: + annotations = ( + annotationlib.call_annotate_function( + annotate, format=annotationlib.Format.VALUE + ) + or {} + ) fields = {n for n in attrs if isinstance(attrs[n], Field)} fields.update(annotations.keys()) field_defaults = {} diff --git a/noxfile.py b/noxfile.py index d5a6099e7..01de5e4b4 100644 --- a/noxfile.py +++ b/noxfile.py @@ -44,14 +44,14 @@ def pytest_argv(): ] -@nox.session(python=["3.9", "3.10", "3.11", "3.12", "3.13"]) +@nox.session(python=["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]) def test(session): session.install("-e", ".[dev]", env=INSTALL_ENV, silent=False) session.run(*pytest_argv(), *session.posargs) -@nox.session(python=["3.9", "3.13"]) +@nox.session(python=["3.10", "3.14"]) def test_otel(session): session.install( ".[dev]", diff --git a/pyproject.toml b/pyproject.toml index a8e5ead9e..6b4915106 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,7 @@ dev = [ "orjson", "numpy", "simsimd", - "pyarrow", + "pyarrow; python_version<'3.14'", "pandas", "mapbox-vector-tile", "jinja2", From 7ad1b60341e694087953c50b518651d58303aebf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 15:01:29 +0100 Subject: [PATCH 06/36] Add `flush_after_seconds` option to `streaming_bulk()` (#3064) (#3117) * Add flush option to streaming_bulk() * unit tests * bulk timeouts * use context manager to run the timeout background tasks * format code * integration tests * docstrings (cherry picked from commit 6fbdecb7219c708870af1985991264d753f66496) Co-authored-by: Miguel Grinberg --- elasticsearch/_async/helpers.py | 67 +++++++-- elasticsearch/compat.py | 44 +++++- elasticsearch/helpers/__init__.py | 11 +- elasticsearch/helpers/actions.py | 139 +++++++++++++----- .../test_async/test_server/test_helpers.py | 40 +++++ test_elasticsearch/test_helpers.py | 50 ++++++- .../test_server/test_helpers.py | 42 ++++++ 7 files changed, 343 insertions(+), 50 deletions(-) diff --git a/elasticsearch/_async/helpers.py b/elasticsearch/_async/helpers.py index e4d5e6bc5..c9243af63 100644 --- a/elasticsearch/_async/helpers.py +++ b/elasticsearch/_async/helpers.py @@ -33,12 +33,16 @@ Union, ) +from ..compat import safe_task from ..exceptions import ApiError, NotFoundError, TransportError from ..helpers.actions import ( _TYPE_BULK_ACTION, _TYPE_BULK_ACTION_BODY, _TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_HEADER_AND_BODY, + _TYPE_BULK_ACTION_HEADER_WITH_META_AND_BODY, + _TYPE_BULK_ACTION_WITH_META, + BulkMeta, _ActionChunker, _process_bulk_chunk_error, _process_bulk_chunk_success, @@ -54,9 +58,10 @@ async def _chunk_actions( - actions: AsyncIterable[_TYPE_BULK_ACTION_HEADER_AND_BODY], + actions: AsyncIterable[_TYPE_BULK_ACTION_HEADER_WITH_META_AND_BODY], chunk_size: int, max_chunk_bytes: int, + flush_after_seconds: Optional[float], serializer: Serializer, ) -> AsyncIterable[ Tuple[ @@ -76,10 +81,42 @@ async def _chunk_actions( chunker = _ActionChunker( chunk_size=chunk_size, max_chunk_bytes=max_chunk_bytes, serializer=serializer ) - async for action, data in actions: - ret = chunker.feed(action, data) - if ret: - yield ret + + if not flush_after_seconds: + async for action, data in actions: + ret = chunker.feed(action, data) + if ret: + yield ret + else: + item_queue: asyncio.Queue[_TYPE_BULK_ACTION_HEADER_WITH_META_AND_BODY] = ( + asyncio.Queue() + ) + + async def get_items() -> None: + try: + async for item in actions: + await item_queue.put(item) + finally: + await item_queue.put((BulkMeta.done, None)) + + async with safe_task(get_items()): + timeout: Optional[float] = flush_after_seconds + while True: + try: + action, data = await asyncio.wait_for( + item_queue.get(), timeout=timeout + ) + timeout = flush_after_seconds + except asyncio.TimeoutError: + action, data = BulkMeta.flush, None + timeout = None + + if action is BulkMeta.done: + break + ret = chunker.feed(action, data) + if ret: + yield ret + ret = chunker.flush() if ret: yield ret @@ -159,9 +196,13 @@ async def azip( async def async_streaming_bulk( client: AsyncElasticsearch, - actions: Union[Iterable[_TYPE_BULK_ACTION], AsyncIterable[_TYPE_BULK_ACTION]], + actions: Union[ + Iterable[_TYPE_BULK_ACTION_WITH_META], + AsyncIterable[_TYPE_BULK_ACTION_WITH_META], + ], chunk_size: int = 500, max_chunk_bytes: int = 100 * 1024 * 1024, + flush_after_seconds: Optional[float] = None, raise_on_error: bool = True, expand_action_callback: Callable[ [_TYPE_BULK_ACTION], _TYPE_BULK_ACTION_HEADER_AND_BODY @@ -194,6 +235,9 @@ async def async_streaming_bulk( :arg actions: iterable or async iterable containing the actions to be executed :arg chunk_size: number of docs in one chunk sent to es (default: 500) :arg max_chunk_bytes: the maximum size of the request in bytes (default: 100MB) + :arg flush_after_seconds: time in seconds after which a chunk is written even + if hasn't reached `chunk_size` or `max_chunk_bytes`. Set to 0 to not use a + timeout-based flush. (default: 0) :arg raise_on_error: raise ``BulkIndexError`` containing errors (as `.errors`) from the execution of the last chunk when some occur. By default we raise. :arg raise_on_exception: if ``False`` then don't propagate exceptions from @@ -220,9 +264,14 @@ async def async_streaming_bulk( if isinstance(retry_on_status, int): retry_on_status = (retry_on_status,) - async def map_actions() -> AsyncIterable[_TYPE_BULK_ACTION_HEADER_AND_BODY]: + async def map_actions() -> ( + AsyncIterable[_TYPE_BULK_ACTION_HEADER_WITH_META_AND_BODY] + ): async for item in aiter(actions): - yield expand_action_callback(item) + if isinstance(item, BulkMeta): + yield item, None + else: + yield expand_action_callback(item) serializer = client.transport.serializers.get_serializer("application/json") @@ -234,7 +283,7 @@ async def map_actions() -> AsyncIterable[_TYPE_BULK_ACTION_HEADER_AND_BODY]: ] bulk_actions: List[bytes] async for bulk_data, bulk_actions in _chunk_actions( - map_actions(), chunk_size, max_chunk_bytes, serializer + map_actions(), chunk_size, max_chunk_bytes, flush_after_seconds, serializer ): for attempt in range(max_retries + 1): to_retry: List[bytes] = [] diff --git a/elasticsearch/compat.py b/elasticsearch/compat.py index 007971306..b44b9daea 100644 --- a/elasticsearch/compat.py +++ b/elasticsearch/compat.py @@ -15,11 +15,14 @@ # specific language governing permissions and limitations # under the License. +import asyncio import inspect import os import sys +from contextlib import asynccontextmanager, contextmanager from pathlib import Path -from typing import Tuple, Type, Union +from threading import Thread +from typing import Any, AsyncIterator, Callable, Coroutine, Iterator, Tuple, Type, Union string_types: Tuple[Type[str], Type[bytes]] = (str, bytes) @@ -76,9 +79,48 @@ def warn_stacklevel() -> int: return 0 +@contextmanager +def safe_thread( + target: Callable[..., Any], *args: Any, **kwargs: Any +) -> Iterator[Thread]: + """Run a thread within a context manager block. + + The thread is automatically joined when the block ends. If the thread raised + an exception, it is raised in the caller's context. + """ + captured_exception = None + + def run() -> None: + try: + target(*args, **kwargs) + except BaseException as exc: + nonlocal captured_exception + captured_exception = exc + + thread = Thread(target=run) + thread.start() + yield thread + thread.join() + if captured_exception: + raise captured_exception + + +@asynccontextmanager +async def safe_task(coro: Coroutine[Any, Any, Any]) -> AsyncIterator[asyncio.Task[Any]]: + """Run a background task within a context manager block. + + The task is awaited when the block ends. + """ + task = asyncio.create_task(coro) + yield task + await task + + __all__ = [ "string_types", "to_str", "to_bytes", "warn_stacklevel", + "safe_thread", + "safe_task", ] diff --git a/elasticsearch/helpers/__init__.py b/elasticsearch/helpers/__init__.py index 67676932b..6f8f24c21 100644 --- a/elasticsearch/helpers/__init__.py +++ b/elasticsearch/helpers/__init__.py @@ -19,12 +19,21 @@ from .._utils import fixup_module_metadata from .actions import _chunk_actions # noqa: F401 from .actions import _process_bulk_chunk # noqa: F401 -from .actions import bulk, expand_action, parallel_bulk, reindex, scan, streaming_bulk +from .actions import ( + BULK_FLUSH, + bulk, + expand_action, + parallel_bulk, + reindex, + scan, + streaming_bulk, +) from .errors import BulkIndexError, ScanError __all__ = [ "BulkIndexError", "ScanError", + "BULK_FLUSH", "expand_action", "streaming_bulk", "bulk", diff --git a/elasticsearch/helpers/actions.py b/elasticsearch/helpers/actions.py index d1a43a8dc..79197a1e4 100644 --- a/elasticsearch/helpers/actions.py +++ b/elasticsearch/helpers/actions.py @@ -16,9 +16,10 @@ # under the License. import logging +import queue import time +from enum import Enum from operator import methodcaller -from queue import Queue from typing import ( Any, Callable, @@ -37,13 +38,21 @@ from elastic_transport import OpenTelemetrySpan from .. import Elasticsearch -from ..compat import to_bytes +from ..compat import safe_thread, to_bytes from ..exceptions import ApiError, NotFoundError, TransportError from ..serializer import Serializer from .errors import BulkIndexError, ScanError logger = logging.getLogger("elasticsearch.helpers") + +class BulkMeta(Enum): + flush = 1 + done = 2 + + +BULK_FLUSH = BulkMeta.flush + _TYPE_BULK_ACTION = Union[bytes, str, Dict[str, Any]] _TYPE_BULK_ACTION_HEADER = Dict[str, Any] _TYPE_BULK_ACTION_BODY = Union[None, bytes, Dict[str, Any]] @@ -51,6 +60,13 @@ _TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY ] +_TYPE_BULK_ACTION_WITH_META = Union[bytes, str, Dict[str, Any], BulkMeta] +_TYPE_BULK_ACTION_HEADER_WITH_META = Union[Dict[str, Any], BulkMeta] +_TYPE_BULK_ACTION_HEADER_WITH_META_AND_BODY = Union[ + Tuple[_TYPE_BULK_ACTION_HEADER, _TYPE_BULK_ACTION_BODY], + Tuple[BulkMeta, Any], +] + def expand_action(data: _TYPE_BULK_ACTION) -> _TYPE_BULK_ACTION_HEADER_AND_BODY: """ @@ -139,7 +155,9 @@ def __init__( ] = [] def feed( - self, action: _TYPE_BULK_ACTION_HEADER, data: _TYPE_BULK_ACTION_BODY + self, + action: _TYPE_BULK_ACTION_HEADER_WITH_META, + data: _TYPE_BULK_ACTION_BODY, ) -> Optional[ Tuple[ List[ @@ -152,23 +170,25 @@ def feed( ] ]: ret = None - raw_action = action - raw_data = data - action_bytes = to_bytes(self.serializer.dumps(action), "utf-8") - # +1 to account for the trailing new line character - cur_size = len(action_bytes) + 1 - - data_bytes: Optional[bytes] - if data is not None: - data_bytes = to_bytes(self.serializer.dumps(data), "utf-8") - cur_size += len(data_bytes) + 1 - else: - data_bytes = None + action_bytes = b"" + data_bytes: Optional[bytes] = None + cur_size = 0 + if not isinstance(action, BulkMeta): + action_bytes = to_bytes(self.serializer.dumps(action), "utf-8") + # +1 to account for the trailing new line character + cur_size = len(action_bytes) + 1 + + if data is not None: + data_bytes = to_bytes(self.serializer.dumps(data), "utf-8") + cur_size += len(data_bytes) + 1 + else: + data_bytes = None # full chunk, send it and start a new one if self.bulk_actions and ( self.size + cur_size > self.max_chunk_bytes or self.action_count == self.chunk_size + or (action == BulkMeta.flush and self.bulk_actions) ): ret = (self.bulk_data, self.bulk_actions) self.bulk_actions = [] @@ -176,15 +196,16 @@ def feed( self.size = 0 self.action_count = 0 - self.bulk_actions.append(action_bytes) - if data_bytes is not None: - self.bulk_actions.append(data_bytes) - self.bulk_data.append((raw_action, raw_data)) - else: - self.bulk_data.append((raw_action,)) + if not isinstance(action, BulkMeta): + self.bulk_actions.append(action_bytes) + if data_bytes is not None: + self.bulk_actions.append(data_bytes) + self.bulk_data.append((action, data)) + else: + self.bulk_data.append((action,)) - self.size += cur_size - self.action_count += 1 + self.size += cur_size + self.action_count += 1 return ret def flush( @@ -209,9 +230,10 @@ def flush( def _chunk_actions( - actions: Iterable[_TYPE_BULK_ACTION_HEADER_AND_BODY], + actions: Iterable[_TYPE_BULK_ACTION_HEADER_WITH_META_AND_BODY], chunk_size: int, max_chunk_bytes: int, + flush_after_seconds: Optional[float], serializer: Serializer, ) -> Iterable[ Tuple[ @@ -231,10 +253,41 @@ def _chunk_actions( chunker = _ActionChunker( chunk_size=chunk_size, max_chunk_bytes=max_chunk_bytes, serializer=serializer ) - for action, data in actions: - ret = chunker.feed(action, data) - if ret: - yield ret + + if not flush_after_seconds: + for action, data in actions: + ret = chunker.feed(action, data) + if ret: + yield ret + else: + item_queue: queue.Queue[_TYPE_BULK_ACTION_HEADER_WITH_META_AND_BODY] = ( + queue.Queue() + ) + + def get_items() -> None: + try: + for item in actions: + item_queue.put(item) + finally: + # make sure we signal the end even if there is an exception + item_queue.put((BulkMeta.done, None)) + + with safe_thread(get_items): + timeout: Optional[float] = flush_after_seconds + while True: + try: + action, data = item_queue.get(timeout=timeout) + timeout = flush_after_seconds + except queue.Empty: + action, data = BulkMeta.flush, None + timeout = None + + if action is BulkMeta.done: + break + ret = chunker.feed(action, data) + if ret: + yield ret + ret = chunker.flush() if ret: yield ret @@ -361,9 +414,10 @@ def _process_bulk_chunk( def streaming_bulk( client: Elasticsearch, - actions: Iterable[_TYPE_BULK_ACTION], + actions: Iterable[_TYPE_BULK_ACTION_WITH_META], chunk_size: int = 500, max_chunk_bytes: int = 100 * 1024 * 1024, + flush_after_seconds: Optional[float] = None, raise_on_error: bool = True, expand_action_callback: Callable[ [_TYPE_BULK_ACTION], _TYPE_BULK_ACTION_HEADER_AND_BODY @@ -397,6 +451,9 @@ def streaming_bulk( :arg actions: iterable containing the actions to be executed :arg chunk_size: number of docs in one chunk sent to es (default: 500) :arg max_chunk_bytes: the maximum size of the request in bytes (default: 100MB) + :arg flush_after_seconds: time in seconds after which a chunk is written even + if hasn't reached `chunk_size` or `max_chunk_bytes`. Set to 0 to not use a + timeout-based flush. (default: 0) :arg raise_on_error: raise ``BulkIndexError`` containing errors (as `.errors`) from the execution of the last chunk when some occur. By default we raise. :arg raise_on_exception: if ``False`` then don't propagate exceptions from @@ -425,6 +482,13 @@ def streaming_bulk( serializer = client.transport.serializers.get_serializer("application/json") + def expand_action_with_meta( + data: _TYPE_BULK_ACTION_WITH_META, + ) -> _TYPE_BULK_ACTION_HEADER_WITH_META_AND_BODY: + if isinstance(data, BulkMeta): + return data, None + return expand_action_callback(data) + bulk_data: List[ Union[ Tuple[_TYPE_BULK_ACTION_HEADER], @@ -433,9 +497,10 @@ def streaming_bulk( ] bulk_actions: List[bytes] for bulk_data, bulk_actions in _chunk_actions( - map(expand_action_callback, actions), + map(expand_action_with_meta, actions), chunk_size, max_chunk_bytes, + flush_after_seconds, serializer, ): for attempt in range(max_retries + 1): @@ -557,6 +622,7 @@ def parallel_bulk( thread_count: int = 4, chunk_size: int = 500, max_chunk_bytes: int = 100 * 1024 * 1024, + flush_after_seconds: Optional[float] = None, queue_size: int = 4, expand_action_callback: Callable[ [_TYPE_BULK_ACTION], _TYPE_BULK_ACTION_HEADER_AND_BODY @@ -573,6 +639,9 @@ def parallel_bulk( :arg thread_count: size of the threadpool to use for the bulk requests :arg chunk_size: number of docs in one chunk sent to es (default: 500) :arg max_chunk_bytes: the maximum size of the request in bytes (default: 100MB) + :arg flush_after_seconds: time in seconds after which a chunk is written even + if hasn't reached `chunk_size` or `max_chunk_bytes`. Set to 0 to not use a + timeout-based flush. (default: 0) :arg raise_on_error: raise ``BulkIndexError`` containing errors (as `.errors`) from the execution of the last chunk when some occur. By default we raise. :arg raise_on_exception: if ``False`` then don't propagate exceptions from @@ -596,7 +665,7 @@ def _setup_queues(self) -> None: super()._setup_queues() # type: ignore[misc] # The queue must be at least the size of the number of threads to # prevent hanging when inserting sentinel values during teardown. - self._inqueue: Queue[ + self._inqueue: queue.Queue[ Tuple[ List[ Union[ @@ -605,7 +674,7 @@ def _setup_queues(self) -> None: ], List[bytes], ] - ] = Queue(max(queue_size, thread_count)) + ] = queue.Queue(max(queue_size, thread_count)) self._quick_put = self._inqueue.put with client._otel.helpers_span("helpers.parallel_bulk") as otel_span: @@ -625,7 +694,11 @@ def _setup_queues(self) -> None: ) ), _chunk_actions( - expanded_actions, chunk_size, max_chunk_bytes, serializer + expanded_actions, + chunk_size, + max_chunk_bytes, + flush_after_seconds, + serializer, ), ): yield from result diff --git a/test_elasticsearch/test_async/test_server/test_helpers.py b/test_elasticsearch/test_async/test_server/test_helpers.py index a235784be..219b81b83 100644 --- a/test_elasticsearch/test_async/test_server/test_helpers.py +++ b/test_elasticsearch/test_async/test_server/test_helpers.py @@ -17,6 +17,7 @@ import asyncio import logging +import time from datetime import datetime, timedelta, timezone from unittest.mock import MagicMock, call, patch @@ -123,6 +124,45 @@ def sync_gen(): "_source" ] + async def test_explicit_flushes(self, async_client): + async def async_gen(): + yield {"answer": 2, "_id": 0} + yield {"answer": 1, "_id": 1} + yield helpers.BULK_FLUSH + await asyncio.sleep(0.5) + yield {"answer": 2, "_id": 2} + + timestamps = [] + async for ok, item in helpers.async_streaming_bulk( + async_client, async_gen(), index="test-index", refresh=True + ): + timestamps.append(time.time()) + assert ok + + # make sure there is a pause between the writing of the 2nd and 3rd items + assert timestamps[2] - timestamps[1] > (timestamps[1] - timestamps[0]) * 2 + + async def test_timeout_flushes(self, async_client): + async def async_gen(): + yield {"answer": 2, "_id": 0} + yield {"answer": 1, "_id": 1} + await asyncio.sleep(0.5) + yield {"answer": 2, "_id": 2} + + timestamps = [] + async for ok, item in helpers.async_streaming_bulk( + async_client, + async_gen(), + index="test-index", + refresh=True, + flush_after_seconds=0.05, + ): + assert ok + timestamps.append(time.time()) + + # make sure there is a pause between the writing of the 2nd and 3rd items + assert timestamps[2] - timestamps[1] > (timestamps[1] - timestamps[0]) * 2 + async def test_all_errors_from_chunk_are_raised_on_failure(self, async_client): await async_client.indices.create( index="i", diff --git a/test_elasticsearch/test_helpers.py b/test_elasticsearch/test_helpers.py index e30635f44..398cb6cc3 100644 --- a/test_elasticsearch/test_helpers.py +++ b/test_elasticsearch/test_helpers.py @@ -18,6 +18,7 @@ import pickle import threading import time +from typing import Optional from unittest import mock import pytest @@ -156,21 +157,34 @@ def test__source_metadata_or_source(self): {"_source": {"key2": "val2"}, "key": "val", "_op_type": "update"} ) == ({"update": {}}, {"key2": "val2"}) - def test_chunks_are_chopped_by_byte_size(self): + @pytest.mark.parametrize("flush_seconds", [None, 10]) + def test_chunks_are_chopped_by_byte_size(self, flush_seconds: Optional[float]): assert 100 == len( - list(helpers._chunk_actions(self.actions, 100000, 1, JSONSerializer())) + list( + helpers._chunk_actions( + self.actions, 100000, 1, flush_seconds, JSONSerializer() + ) + ) ) - def test_chunks_are_chopped_by_chunk_size(self): + @pytest.mark.parametrize("flush_seconds", [None, 10]) + def test_chunks_are_chopped_by_chunk_size(self, flush_seconds: Optional[float]): assert 10 == len( - list(helpers._chunk_actions(self.actions, 10, 99999999, JSONSerializer())) + list( + helpers._chunk_actions( + self.actions, 10, 99999999, flush_seconds, JSONSerializer() + ) + ) ) - def test_chunks_are_chopped_by_byte_size_properly(self): + @pytest.mark.parametrize("flush_seconds", [None, 10]) + def test_chunks_are_chopped_by_byte_size_properly( + self, flush_seconds: Optional[float] + ): max_byte_size = 170 chunks = list( helpers._chunk_actions( - self.actions, 100000, max_byte_size, JSONSerializer() + self.actions, 100000, max_byte_size, flush_seconds, JSONSerializer() ) ) assert 25 == len(chunks) @@ -178,6 +192,30 @@ def test_chunks_are_chopped_by_byte_size_properly(self): chunk = b"".join(chunk_actions) assert len(chunk) <= max_byte_size + @pytest.mark.parametrize("flush_seconds", [None, 10]) + def test_chunks_are_chopped_by_flush(self, flush_seconds: Optional[float]): + flush = (helpers.BULK_FLUSH, None) + actions = ( + self.actions[:3] + + [flush] * 2 # two consecutive flushes after 3 items + + self.actions[3:4] + + [flush] # flush after one more item + + self.actions[4:] + + [flush] # flush at the end + ) + chunks = list( + helpers._chunk_actions( + actions, 100, 99999999, flush_seconds, JSONSerializer() + ) + ) + assert 3 == len(chunks) + assert len(chunks[0][0]) == 3 + assert len(chunks[0][1]) == 6 + assert len(chunks[1][0]) == 1 + assert len(chunks[1][1]) == 2 + assert len(chunks[2][0]) == 96 + assert len(chunks[2][1]) == 192 + class TestExpandActions: @pytest.mark.parametrize("action", ["whatever", b"whatever"]) diff --git a/test_elasticsearch/test_server/test_helpers.py b/test_elasticsearch/test_server/test_helpers.py index 74b9f0ef8..f175c6e0b 100644 --- a/test_elasticsearch/test_server/test_helpers.py +++ b/test_elasticsearch/test_server/test_helpers.py @@ -16,6 +16,7 @@ # under the License. import json +import time from datetime import datetime, timedelta from unittest.mock import call, patch @@ -75,6 +76,47 @@ def test_bulk_all_documents_get_inserted(sync_client): assert {"answer": 42} == sync_client.get(index="test-index", id=42)["_source"] +def test_explicit_flushes(sync_client): + def sync_gen(): + yield {"answer": 0, "_id": 0} + yield {"answer": 1, "_id": 1} + yield helpers.BULK_FLUSH + time.sleep(0.5) + yield {"answer": 2, "_id": 2} + + timestamps = [] + for ok, item in helpers.streaming_bulk( + sync_client, sync_gen(), index="test-index", refresh=True + ): + assert ok + timestamps.append(time.time()) + + # make sure there is a pause between the writing of the 2nd and 3rd items + assert timestamps[2] - timestamps[1] > (timestamps[1] - timestamps[0]) * 2 + + +def test_timeout_flushes(sync_client): + def sync_gen(): + yield {"answer": 0, "_id": 0} + yield {"answer": 1, "_id": 1} + time.sleep(0.5) + yield {"answer": 2, "_id": 2} + + timestamps = [] + for ok, item in helpers.streaming_bulk( + sync_client, + sync_gen(), + index="test-index", + refresh=True, + flush_after_seconds=0.05, + ): + assert ok + timestamps.append(time.time()) + + # make sure there is a pause between the writing of the 2nd and 3rd items + assert timestamps[2] - timestamps[1] > (timestamps[1] - timestamps[0]) * 2 + + def test_bulk_all_errors_from_chunk_are_raised_on_failure(sync_client): sync_client.indices.create( index="i", From 7cadb9f58359c5e43799fb245e48b5a6138d4f59 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 16:28:47 +0100 Subject: [PATCH 07/36] Drop Python 3.9 support (#3114) (#3118) * Drop Python 3.9 support * more references to 3.9 updated (cherry picked from commit c275dedbe4d3f6c54442a8cc45ea6aca5b54daa6) Co-authored-by: Miguel Grinberg --- .buildkite/pipeline.yml | 1 - .github/workflows/ci.yml | 2 +- CONTRIBUTING.md | 2 +- docs/reference/getting-started.md | 2 +- examples/fastapi-apm/dockerfiles/Dockerfile.app | 2 +- examples/fastapi-apm/dockerfiles/Dockerfile.ping | 2 +- noxfile.py | 2 +- pyproject.toml | 3 +-- utils/generate-examples.py | 2 +- utils/run-unasync-dsl.py | 2 +- 10 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 729336bac..33eb810a0 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -11,7 +11,6 @@ steps: matrix: setup: python: - - "3.9" - "3.10" - "3.11" - "3.12" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20663c601..da7797f91 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] nox-session: [""] runs-on: ["ubuntu-latest"] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4d83ec97c..512ba2a7d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ found at `.buildkite/run-elasticsearch.sh`. There are several environment variables that control integration tests: -- `PYTHON_VERSION`: Version of Python to use, defaults to `3.9` +- `PYTHON_VERSION`: Version of Python to use, defaults to `3.14` - `PYTHON_CONNECTION_CLASS`: Connection class to use, defaults to `Urllib3HttpConnection` - `STACK_VERSION`: Version of Elasticsearch to use. These should be the same as tags of `docker.elastic.co/elasticsearch/elasticsearch` diff --git a/docs/reference/getting-started.md b/docs/reference/getting-started.md index 7a5d82c16..151cb95fb 100644 --- a/docs/reference/getting-started.md +++ b/docs/reference/getting-started.md @@ -11,7 +11,7 @@ This page guides you through the installation process of the Python client, show ### Requirements [_requirements] -* [Python](https://www.python.org/) 3.9 or newer +* [Python](https://www.python.org/) 3.10 or newer * [`pip`](https://pip.pypa.io/en/stable/), installed by default alongside Python diff --git a/examples/fastapi-apm/dockerfiles/Dockerfile.app b/examples/fastapi-apm/dockerfiles/Dockerfile.app index 40809ceba..c4c9fe38f 100644 --- a/examples/fastapi-apm/dockerfiles/Dockerfile.app +++ b/examples/fastapi-apm/dockerfiles/Dockerfile.app @@ -1,4 +1,4 @@ -FROM python:3.9 +FROM python:3.10 EXPOSE 9292 WORKDIR / diff --git a/examples/fastapi-apm/dockerfiles/Dockerfile.ping b/examples/fastapi-apm/dockerfiles/Dockerfile.ping index 97e24af2d..6525cbea4 100644 --- a/examples/fastapi-apm/dockerfiles/Dockerfile.ping +++ b/examples/fastapi-apm/dockerfiles/Dockerfile.ping @@ -1,4 +1,4 @@ -FROM python:3.9 +FROM python:3.10 WORKDIR / RUN python -m pip install \ --no-cache \ diff --git a/noxfile.py b/noxfile.py index 01de5e4b4..73f3df3d5 100644 --- a/noxfile.py +++ b/noxfile.py @@ -44,7 +44,7 @@ def pytest_argv(): ] -@nox.session(python=["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]) +@nox.session(python=["3.10", "3.11", "3.12", "3.13", "3.14"]) def test(session): session.install("-e", ".[dev]", env=INSTALL_ENV, silent=False) diff --git a/pyproject.toml b/pyproject.toml index 6b4915106..e2453c8ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "elasticsearch" description = "Python client for Elasticsearch" readme = "README.md" license = "Apache-2.0" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "Elastic Client Library Maintainers", email = "client-libs@elastic.co" }, ] @@ -21,7 +21,6 @@ classifiers = [ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/utils/generate-examples.py b/utils/generate-examples.py index 39fdc0973..20c5c3424 100644 --- a/utils/generate-examples.py +++ b/utils/generate-examples.py @@ -198,7 +198,7 @@ def blacken(filename): runner = CliRunner() result = runner.invoke( - black.main, [str(filename), "--line-length=75", "--target-version=py37"] + black.main, [str(filename), "--line-length=75", "--target-version=py310"] ) assert result.exit_code == 0, result.output diff --git a/utils/run-unasync-dsl.py b/utils/run-unasync-dsl.py index b74c748fa..f9035de76 100644 --- a/utils/run-unasync-dsl.py +++ b/utils/run-unasync-dsl.py @@ -101,7 +101,7 @@ def main(check=False): output_dirs = [] for dir in source_dirs: output_dirs.append(f"{dir[0]}_sync_check/" if check else dir[1]) - subprocess.check_call(["black", "--target-version=py38", *output_dirs]) + subprocess.check_call(["black", "--target-version=py310", *output_dirs]) subprocess.check_call(["isort", *output_dirs]) for dir, output_dir in zip(source_dirs, output_dirs): for file in glob("*.py", root_dir=dir[0]): From 6fa00e291bad31e0cf531bc4aa6a5ce04732d942 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 12:08:20 +0100 Subject: [PATCH 08/36] Support Trio with httpx (#3089) (#3119) * Support trio with httpx * Fix lint * Stop trying to run clients tests twice in a row * Remove anyio from runtime dependencies * Use custom _sleep function * Stop pretending that we want/can run YAML test with both trio and asyncio * Document Trio support * Remove debug print * Remove useless anyio fixture * Handle more markers * Fix references to asyncio * Use anyio for bulk flush timeout * linter fixes * revert pyproject.toml to official transport releases --------- (cherry picked from commit 419c1ff94c42afee98e70e99cbaf4d6f5c80526c) Co-authored-by: Quentin Pradet Co-authored-by: Miguel Grinberg --- docs/reference/async.md | 21 +++++ docs/reference/dsl_how_to_guides.md | 16 +++- elasticsearch/_async/helpers.py | 43 ++++++---- elasticsearch/compat.py | 17 +--- pyproject.toml | 6 +- .../test_async/test_server/conftest.py | 17 ++-- .../test_async/test_server/test_clients.py | 2 +- .../test_async/test_server/test_helpers.py | 19 ++--- .../test_server/test_mapbox_vector_tile.py | 5 +- .../test_server/test_rest_api_spec.py | 5 +- .../test_async/test_transport.py | 48 +++++------ .../test_dsl/_async/test_document.py | 6 +- .../test_dsl/_async/test_index.py | 2 +- .../test_dsl/_async/test_search.py | 12 +-- .../test_dsl/_async/test_update_by_query.py | 2 +- test_elasticsearch/test_dsl/async_sleep.py | 5 +- test_elasticsearch/test_dsl/conftest.py | 46 ++++++----- .../test_integration/_async/test_analysis.py | 6 +- .../test_integration/_async/test_document.py | 82 +++++++++---------- .../test_integration/_async/test_esql.py | 4 +- .../_async/test_faceted_search.py | 18 ++-- .../test_integration/_async/test_index.py | 14 ++-- .../test_integration/_async/test_mapping.py | 8 +- .../test_integration/_async/test_search.py | 32 ++++---- .../_async/test_update_by_query.py | 6 +- .../_async/test_alias_migration.py | 2 +- .../test_examples/_async/test_completion.py | 2 +- .../_async/test_composite_aggs.py | 4 +- .../test_examples/_async/test_parent_child.py | 7 +- .../test_examples/_async/test_percolate.py | 2 +- .../test_examples/_async/test_vectors.py | 2 +- .../test_server/test_rest_api_spec.py | 2 + utils/run-unasync-dsl.py | 20 ++--- 33 files changed, 263 insertions(+), 220 deletions(-) diff --git a/docs/reference/async.md b/docs/reference/async.md index 70e8949d5..b151150e3 100644 --- a/docs/reference/async.md +++ b/docs/reference/async.md @@ -50,6 +50,27 @@ All APIs that are available under the sync client are also available under the a See also the [Using OpenTelemetry](/reference/opentelemetry.md) page. +## Trio support + +If you prefer using Trio instead of asyncio to take advantage of its better structured concurrency support, you can use the HTTPX async node which supports Trio out of the box. + +```python +import trio +from elasticsearch import AsyncElasticsearch + +client = AsyncElasticsearch( + "https://.../", + api_key="...", + node_class="httpxasync") + +async def main(): + resp = await client.info() + print(resp.body) + +trio.run(main) +``` + +The one limitation of Trio support is that it does not currently support node sniffing, which was not implemented with structured concurrency in mind. ## Frequently Asked Questions [_frequently_asked_questions] diff --git a/docs/reference/dsl_how_to_guides.md b/docs/reference/dsl_how_to_guides.md index 5f0884c3c..66137c1ff 100644 --- a/docs/reference/dsl_how_to_guides.md +++ b/docs/reference/dsl_how_to_guides.md @@ -1555,6 +1555,12 @@ The DSL module supports async/await with [asyncio](https://docs.python.org/3/lib $ python -m pip install "elasticsearch[async]" ``` +The DSL module also supports [Trio](https://trio.readthedocs.io/en/stable/) when using the Async HTTPX client. You do need to install Trio and HTTPX separately: + +```bash +$ python -m pip install "elasticsearch trio httpx" +``` + ### Connections [_connections] Use the `async_connections` module to manage your asynchronous connections. @@ -1565,6 +1571,14 @@ from elasticsearch.dsl import async_connections async_connections.create_connection(hosts=['localhost'], timeout=20) ``` +If you're using Trio, you need to explicitly request the Async HTTP client: + +```python +from elasticsearch.dsl import async_connections + +async_connections.create_connection(hosts=['localhost'], node_class="httpxasync") +``` + All the options available in the `connections` module can be used with `async_connections`. #### How to avoid *Unclosed client session / connector* warnings on exit [_how_to_avoid_unclosed_client_session_connector_warnings_on_exit] @@ -1576,8 +1590,6 @@ es = async_connections.get_connection() await es.close() ``` - - ### Search DSL [_search_dsl] Use the `AsyncSearch` class to perform asynchronous searches. diff --git a/elasticsearch/_async/helpers.py b/elasticsearch/_async/helpers.py index c9243af63..061ba2414 100644 --- a/elasticsearch/_async/helpers.py +++ b/elasticsearch/_async/helpers.py @@ -33,7 +33,9 @@ Union, ) -from ..compat import safe_task +import sniffio +from anyio import create_memory_object_stream, create_task_group, move_on_after + from ..exceptions import ApiError, NotFoundError, TransportError from ..helpers.actions import ( _TYPE_BULK_ACTION, @@ -57,6 +59,15 @@ T = TypeVar("T") +async def _sleep(seconds: float) -> None: + if sniffio.current_async_library() == "trio": + import trio + + await trio.sleep(seconds) + else: + await asyncio.sleep(seconds) + + async def _chunk_actions( actions: AsyncIterable[_TYPE_BULK_ACTION_HEADER_WITH_META_AND_BODY], chunk_size: int, @@ -82,32 +93,36 @@ async def _chunk_actions( chunk_size=chunk_size, max_chunk_bytes=max_chunk_bytes, serializer=serializer ) + action: _TYPE_BULK_ACTION_WITH_META + data: _TYPE_BULK_ACTION_BODY if not flush_after_seconds: async for action, data in actions: ret = chunker.feed(action, data) if ret: yield ret else: - item_queue: asyncio.Queue[_TYPE_BULK_ACTION_HEADER_WITH_META_AND_BODY] = ( - asyncio.Queue() - ) + sender, receiver = create_memory_object_stream[ + _TYPE_BULK_ACTION_HEADER_WITH_META_AND_BODY + ]() async def get_items() -> None: try: async for item in actions: - await item_queue.put(item) + await sender.send(item) finally: - await item_queue.put((BulkMeta.done, None)) + await sender.send((BulkMeta.done, None)) + + async with create_task_group() as tg: + tg.start_soon(get_items) - async with safe_task(get_items()): timeout: Optional[float] = flush_after_seconds while True: - try: - action, data = await asyncio.wait_for( - item_queue.get(), timeout=timeout - ) + action = {} + data = None + with move_on_after(timeout) as scope: + action, data = await receiver.receive() timeout = flush_after_seconds - except asyncio.TimeoutError: + if scope.cancelled_caught: action, data = BulkMeta.flush, None timeout = None @@ -294,9 +309,7 @@ async def map_actions() -> ( ] ] = [] if attempt: - await asyncio.sleep( - min(max_backoff, initial_backoff * 2 ** (attempt - 1)) - ) + await _sleep(min(max_backoff, initial_backoff * 2 ** (attempt - 1))) try: data: Union[ diff --git a/elasticsearch/compat.py b/elasticsearch/compat.py index b44b9daea..03566d748 100644 --- a/elasticsearch/compat.py +++ b/elasticsearch/compat.py @@ -15,14 +15,13 @@ # specific language governing permissions and limitations # under the License. -import asyncio import inspect import os import sys -from contextlib import asynccontextmanager, contextmanager +from contextlib import contextmanager from pathlib import Path from threading import Thread -from typing import Any, AsyncIterator, Callable, Coroutine, Iterator, Tuple, Type, Union +from typing import Any, Callable, Iterator, Tuple, Type, Union string_types: Tuple[Type[str], Type[bytes]] = (str, bytes) @@ -105,22 +104,10 @@ def run() -> None: raise captured_exception -@asynccontextmanager -async def safe_task(coro: Coroutine[Any, Any, Any]) -> AsyncIterator[asyncio.Task[Any]]: - """Run a background task within a context manager block. - - The task is awaited when the block ends. - """ - task = asyncio.create_task(coro) - yield task - await task - - __all__ = [ "string_types", "to_str", "to_bytes", "warn_stacklevel", "safe_thread", - "safe_task", ] diff --git a/pyproject.toml b/pyproject.toml index e2453c8ce..fe0a9ca6c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,9 +40,11 @@ keywords = [ ] dynamic = ["version"] dependencies = [ - "elastic-transport>=9.1.0,<10", + "elastic-transport>=9.2.0,<10", "python-dateutil", "typing-extensions", + "sniffio", + "anyio", ] [project.optional-dependencies] @@ -55,6 +57,7 @@ vectorstore_mmr = ["numpy>=1", "simsimd>=3"] dev = [ "requests>=2, <3", "aiohttp", + "httpx", "pytest", "pytest-cov", "pytest-mock", @@ -77,6 +80,7 @@ dev = [ "mapbox-vector-tile", "jinja2", "tqdm", + "trio", "mypy", "pyright", "types-python-dateutil", diff --git a/test_elasticsearch/test_async/test_server/conftest.py b/test_elasticsearch/test_async/test_server/conftest.py index 623646e7d..b96811d58 100644 --- a/test_elasticsearch/test_async/test_server/conftest.py +++ b/test_elasticsearch/test_async/test_server/conftest.py @@ -16,27 +16,26 @@ # under the License. import pytest -import pytest_asyncio +import sniffio import elasticsearch from ...utils import CA_CERTS, wipe_cluster -pytestmark = pytest.mark.asyncio - -@pytest_asyncio.fixture(scope="function") +@pytest.fixture(scope="function") async def async_client_factory(elasticsearch_url): - - if not hasattr(elasticsearch, "AsyncElasticsearch"): - pytest.skip("test requires 'AsyncElasticsearch' and aiohttp to be installed") - + kwargs = {} + if sniffio.current_async_library() == "trio": + kwargs["node_class"] = "httpxasync" # Unfortunately the asyncio client needs to be rebuilt every # test execution due to how pytest-asyncio manages # event loops (one per test!) client = None try: - client = elasticsearch.AsyncElasticsearch(elasticsearch_url, ca_certs=CA_CERTS) + client = elasticsearch.AsyncElasticsearch( + elasticsearch_url, ca_certs=CA_CERTS, **kwargs + ) yield client finally: if client: diff --git a/test_elasticsearch/test_async/test_server/test_clients.py b/test_elasticsearch/test_async/test_server/test_clients.py index 00de2c7fb..eace5290f 100644 --- a/test_elasticsearch/test_async/test_server/test_clients.py +++ b/test_elasticsearch/test_async/test_server/test_clients.py @@ -18,7 +18,7 @@ import pytest -pytestmark = pytest.mark.asyncio +pytestmark = pytest.mark.anyio @pytest.mark.parametrize("kwargs", [{"body": {"text": "привет"}}, {"text": "привет"}]) diff --git a/test_elasticsearch/test_async/test_server/test_helpers.py b/test_elasticsearch/test_async/test_server/test_helpers.py index 219b81b83..3c4f933d0 100644 --- a/test_elasticsearch/test_async/test_server/test_helpers.py +++ b/test_elasticsearch/test_async/test_server/test_helpers.py @@ -15,21 +15,20 @@ # specific language governing permissions and limitations # under the License. -import asyncio import logging import time from datetime import datetime, timedelta, timezone from unittest.mock import MagicMock, call, patch +import anyio import pytest -import pytest_asyncio from elastic_transport import ApiResponseMeta, ObjectApiResponse from elasticsearch import helpers from elasticsearch.exceptions import ApiError from elasticsearch.helpers import ScanError -pytestmark = [pytest.mark.asyncio] +pytestmark = pytest.mark.anyio class AsyncMock(MagicMock): @@ -93,7 +92,7 @@ async def test_all_documents_get_inserted(self, async_client): async def test_documents_data_types(self, async_client): async def async_gen(): for x in range(100): - await asyncio.sleep(0) + await anyio.sleep(0) yield {"answer": x, "_id": x} def sync_gen(): @@ -129,7 +128,7 @@ async def async_gen(): yield {"answer": 2, "_id": 0} yield {"answer": 1, "_id": 1} yield helpers.BULK_FLUSH - await asyncio.sleep(0.5) + await anyio.sleep(0.5) yield {"answer": 2, "_id": 2} timestamps = [] @@ -146,7 +145,7 @@ async def test_timeout_flushes(self, async_client): async def async_gen(): yield {"answer": 2, "_id": 0} yield {"answer": 1, "_id": 1} - await asyncio.sleep(0.5) + await anyio.sleep(0.5) yield {"answer": 2, "_id": 2} timestamps = [] @@ -531,7 +530,7 @@ def __await__(self): return self().__await__() -@pytest_asyncio.fixture(scope="function") +@pytest.fixture(scope="function") async def scan_teardown(async_client): yield await async_client.clear_scroll(scroll_id="_all") @@ -955,7 +954,7 @@ async def test_scan_from_keyword_is_aliased(async_client, scan_kwargs): assert "from" not in search_mock.call_args[1] -@pytest_asyncio.fixture(scope="function") +@pytest.fixture(scope="function") async def reindex_setup(async_client): bulk = [] for x in range(100): @@ -1033,7 +1032,7 @@ async def test_all_documents_get_moved(self, async_client, reindex_setup): )["_source"] -@pytest_asyncio.fixture(scope="function") +@pytest.fixture(scope="function") async def parent_reindex_setup(async_client): body = { "settings": {"number_of_shards": 1, "number_of_replicas": 0}, @@ -1094,7 +1093,7 @@ async def test_children_are_reindexed_correctly( } == q -@pytest_asyncio.fixture(scope="function") +@pytest.fixture(scope="function") async def reindex_data_stream_setup(async_client): dt = datetime.now(tz=timezone.utc) bulk = [] diff --git a/test_elasticsearch/test_async/test_server/test_mapbox_vector_tile.py b/test_elasticsearch/test_async/test_server/test_mapbox_vector_tile.py index 4f5dcaec4..94b5eabe0 100644 --- a/test_elasticsearch/test_async/test_server/test_mapbox_vector_tile.py +++ b/test_elasticsearch/test_async/test_server/test_mapbox_vector_tile.py @@ -16,14 +16,13 @@ # under the License. import pytest -import pytest_asyncio from elasticsearch import RequestError -pytestmark = pytest.mark.asyncio +pytestmark = pytest.mark.anyio -@pytest_asyncio.fixture(scope="function") +@pytest.fixture(scope="function") async def mvt_setup(async_client): await async_client.indices.create( index="museums", diff --git a/test_elasticsearch/test_async/test_server/test_rest_api_spec.py b/test_elasticsearch/test_async/test_server/test_rest_api_spec.py index c48262b61..ee148c0cd 100644 --- a/test_elasticsearch/test_async/test_server/test_rest_api_spec.py +++ b/test_elasticsearch/test_async/test_server/test_rest_api_spec.py @@ -25,7 +25,6 @@ import warnings import pytest -import pytest_asyncio from elasticsearch import ElasticsearchWarning, RequestError @@ -39,6 +38,8 @@ ) from ...utils import parse_version +# We're not using `pytest.mark.anyio` here because it would run the test suite twice, +# which does not work as it does not fully clean up after itself. pytestmark = pytest.mark.asyncio XPACK_FEATURES = None @@ -240,7 +241,7 @@ async def _feature_enabled(self, name): return name in XPACK_FEATURES -@pytest_asyncio.fixture(scope="function") +@pytest.fixture(scope="function") def async_runner(async_client_factory): return AsyncYamlRunner(async_client_factory) diff --git a/test_elasticsearch/test_async/test_transport.py b/test_elasticsearch/test_async/test_transport.py index cf9b8600a..c25da05fd 100644 --- a/test_elasticsearch/test_async/test_transport.py +++ b/test_elasticsearch/test_async/test_transport.py @@ -16,11 +16,12 @@ # under the License. -import asyncio import re +import time import warnings from typing import Any, Dict, Optional +import anyio import pytest from elastic_transport import ( ApiResponseMeta, @@ -40,8 +41,6 @@ UnsupportedProductError, ) -pytestmark = pytest.mark.asyncio - class DummyNode(BaseAsyncNode): def __init__(self, config: NodeConfig): @@ -175,6 +174,7 @@ def mark_live(self, connection): }""" +@pytest.mark.anyio class TestTransport: async def test_request_timeout_extracted_from_params_and_passed(self): client = AsyncElasticsearch( @@ -378,6 +378,9 @@ async def test_override_mark_dead_mark_live(self): assert len(client.transport.node_pool._alive_nodes) == 2 assert len(client.transport.node_pool._dead_consecutive_failures) == 0 + +@pytest.mark.asyncio +class TestSniffing: @pytest.mark.parametrize( ["nodes_info_response", "node_host"], [(CLUSTER_NODES, "1.1.1.1"), (CLUSTER_NODES_7x_PUBLISH_HOST, "somehost.tld")], @@ -528,23 +531,22 @@ async def test_sniff_on_node_failure_triggers(self, extra_key, extra_value): assert len(client.transport.node_pool) == 3 async def test_sniff_after_n_seconds(self): - event_loop = asyncio.get_running_loop() client = AsyncElasticsearch( # noqa: F821 [NodeConfig("http", "localhost", 9200, _extras={"data": CLUSTER_NODES})], node_class=DummyNode, min_delay_between_sniffing=5, ) - client.transport._last_sniffed_at = event_loop.time() + client.transport._last_sniffed_at = time.monotonic() await client.info() for _ in range(4): await client.info() - await asyncio.sleep(0) + await anyio.sleep(0) assert 1 == len(client.transport.node_pool) - client.transport._last_sniffed_at = event_loop.time() - 5.1 + client.transport._last_sniffed_at = time.monotonic() - 5.1 await client.info() await client.transport._sniffing_task # Need to wait for the sniffing task to complete @@ -554,9 +556,9 @@ async def test_sniff_after_n_seconds(self): node.base_url for node in client.transport.node_pool.all() ) assert ( - event_loop.time() - 1 + time.monotonic() - 1 < client.transport._last_sniffed_at - < event_loop.time() + 0.01 + < time.monotonic() + 0.01 ) @pytest.mark.parametrize( @@ -581,7 +583,6 @@ async def test_sniffing_disabled_on_elastic_cloud(self, kwargs): ) async def test_sniff_on_start_close_unlocks_async_calls(self): - event_loop = asyncio.get_running_loop() client = AsyncElasticsearch( # noqa: F821 [ NodeConfig( @@ -596,20 +597,17 @@ async def test_sniff_on_start_close_unlocks_async_calls(self): ) # Start making _async_calls() before we cancel - tasks = [] - start_time = event_loop.time() - for _ in range(3): - tasks.append(event_loop.create_task(client.info())) - await asyncio.sleep(0) - - # Close the transport while the sniffing task is active! :( - await client.transport.close() - - # Now we start waiting on all those _async_calls() - await asyncio.gather(*tasks) - end_time = event_loop.time() - duration = end_time - start_time + async with anyio.create_task_group() as tg: + start_time = time.monotonic() + for _ in range(3): + tg.start_soon(client.info) + await anyio.sleep(0) + # Close the transport while the sniffing task is active! :( + await client.transport.close() + + end_time = time.monotonic() + duration = end_time - start_time # A lot quicker than 10 seconds defined in 'delay' assert duration < 1 @@ -661,6 +659,7 @@ def sniffed_node_callback( assert ports == {9200, 124} +@pytest.mark.anyio @pytest.mark.parametrize("headers", [{}, {"X-elastic-product": "BAD HEADER"}]) async def test_unsupported_product_error(headers): client = AsyncElasticsearch( @@ -690,6 +689,7 @@ async def test_unsupported_product_error(headers): ) +@pytest.mark.anyio @pytest.mark.parametrize("status", [401, 403, 413, 500]) async def test_unsupported_product_error_not_raised_on_non_2xx(status): client = AsyncElasticsearch( @@ -709,6 +709,7 @@ async def test_unsupported_product_error_not_raised_on_non_2xx(status): assert e.meta.status == status +@pytest.mark.anyio @pytest.mark.parametrize("status", [404, 500]) async def test_api_error_raised_before_product_error(status): client = AsyncElasticsearch( @@ -737,6 +738,7 @@ async def test_api_error_raised_before_product_error(status): assert calls[0][0] == ("GET", "/") +@pytest.mark.anyio @pytest.mark.parametrize( "headers", [ diff --git a/test_elasticsearch/test_dsl/_async/test_document.py b/test_elasticsearch/test_dsl/_async/test_document.py index 5fe2d326c..8ac0fc887 100644 --- a/test_elasticsearch/test_dsl/_async/test_document.py +++ b/test_elasticsearch/test_dsl/_async/test_document.py @@ -582,21 +582,21 @@ def test_meta_fields_can_be_set_directly_in_init() -> None: assert md.meta.id is p -@pytest.mark.asyncio +@pytest.mark.anyio async def test_save_no_index(async_mock_client: Any) -> None: md = MyDoc() with raises(ValidationException): await md.save(using="mock") -@pytest.mark.asyncio +@pytest.mark.anyio async def test_delete_no_index(async_mock_client: Any) -> None: md = MyDoc() with raises(ValidationException): await md.delete(using="mock") -@pytest.mark.asyncio +@pytest.mark.anyio async def test_update_no_fields() -> None: md = MyDoc() with raises(IllegalOperation): diff --git a/test_elasticsearch/test_dsl/_async/test_index.py b/test_elasticsearch/test_dsl/_async/test_index.py index 624bab79a..e6668e264 100644 --- a/test_elasticsearch/test_dsl/_async/test_index.py +++ b/test_elasticsearch/test_dsl/_async/test_index.py @@ -190,7 +190,7 @@ def test_index_template_can_have_order() -> None: assert {"index_patterns": ["i-*"], "order": 2} == it.to_dict() -@pytest.mark.asyncio +@pytest.mark.anyio async def test_index_template_save_result(async_mock_client: Any) -> None: it = AsyncIndexTemplate("test-template", "test-*") diff --git a/test_elasticsearch/test_dsl/_async/test_search.py b/test_elasticsearch/test_dsl/_async/test_search.py index a00ddf448..645a8d4a6 100644 --- a/test_elasticsearch/test_dsl/_async/test_search.py +++ b/test_elasticsearch/test_dsl/_async/test_search.py @@ -39,7 +39,7 @@ def test_expand__to_dot_is_respected() -> None: assert {"query": {"match": {"a__b": 42}}} == s.to_dict() -@pytest.mark.asyncio +@pytest.mark.anyio async def test_execute_uses_cache() -> None: s = AsyncSearch() r = object() @@ -48,7 +48,7 @@ async def test_execute_uses_cache() -> None: assert r is await s.execute() -@pytest.mark.asyncio +@pytest.mark.anyio async def test_cache_can_be_ignored(async_mock_client: Any) -> None: s = AsyncSearch(using="mock") r = object() @@ -58,7 +58,7 @@ async def test_cache_can_be_ignored(async_mock_client: Any) -> None: async_mock_client.search.assert_awaited_once_with(index=None, body={}) -@pytest.mark.asyncio +@pytest.mark.anyio async def test_iter_iterates_over_hits() -> None: s = AsyncSearch() s._response = [1, 2, 3] # type: ignore[assignment] @@ -612,7 +612,7 @@ def test_from_dict_doesnt_need_query() -> None: assert {"size": 5} == s.to_dict() -@pytest.mark.asyncio +@pytest.mark.anyio async def test_params_being_passed_to_search(async_mock_client: Any) -> None: s = AsyncSearch(using="mock") s = s.params(routing="42") @@ -704,7 +704,7 @@ def test_exclude() -> None: } == s.to_dict() -@pytest.mark.asyncio +@pytest.mark.anyio async def test_delete_by_query(async_mock_client: Any) -> None: s = AsyncSearch(using="mock", index="i").query("match", lang="java") await s.delete() @@ -789,7 +789,7 @@ def test_rescore_query_to_dict() -> None: } -@pytest.mark.asyncio +@pytest.mark.anyio async def test_empty_search() -> None: s = AsyncEmptySearch(index="index-name") s = s.query("match", lang="java") diff --git a/test_elasticsearch/test_dsl/_async/test_update_by_query.py b/test_elasticsearch/test_dsl/_async/test_update_by_query.py index 9253623dc..7e2287db3 100644 --- a/test_elasticsearch/test_dsl/_async/test_update_by_query.py +++ b/test_elasticsearch/test_dsl/_async/test_update_by_query.py @@ -144,7 +144,7 @@ def test_from_dict_doesnt_need_query() -> None: assert {"script": {"source": "test"}} == ubq.to_dict() -@pytest.mark.asyncio +@pytest.mark.anyio async def test_params_being_passed_to_search(async_mock_client: Any) -> None: ubq = AsyncUpdateByQuery(using="mock", index="i") ubq = ubq.params(routing="42") diff --git a/test_elasticsearch/test_dsl/async_sleep.py b/test_elasticsearch/test_dsl/async_sleep.py index ce5ced1c5..84ace53f0 100644 --- a/test_elasticsearch/test_dsl/async_sleep.py +++ b/test_elasticsearch/test_dsl/async_sleep.py @@ -15,10 +15,11 @@ # specific language governing permissions and limitations # under the License. -import asyncio from typing import Union +import anyio + async def sleep(secs: Union[int, float]) -> None: """Tests can use this function to sleep.""" - await asyncio.sleep(secs) + await anyio.sleep(secs) diff --git a/test_elasticsearch/test_dsl/conftest.py b/test_elasticsearch/test_dsl/conftest.py index 5dd83e54c..5951108f4 100644 --- a/test_elasticsearch/test_dsl/conftest.py +++ b/test_elasticsearch/test_dsl/conftest.py @@ -16,7 +16,6 @@ # under the License. -import asyncio import os import re import time @@ -25,9 +24,10 @@ from unittest import SkipTest from unittest.mock import AsyncMock, Mock -import pytest_asyncio +import anyio +import pytest +import sniffio from elastic_transport import ObjectApiResponse -from pytest import fixture, skip from elasticsearch import AsyncElasticsearch, Elasticsearch from elasticsearch.dsl import Search @@ -86,6 +86,10 @@ async def get_async_test_client( if elasticsearch_url.startswith("https://"): kw["ca_certs"] = CA_CERTS + # httpx is the only HTTP client that supports trio + if sniffio.current_async_library() == "trio": + kw["node_class"] = "httpxasync" + kw.update(kwargs) client = AsyncElasticsearch(elasticsearch_url, **kw) @@ -97,7 +101,7 @@ async def get_async_test_client( except ConnectionError: if wait and tries_left == 1: raise - await asyncio.sleep(0.1) + await anyio.sleep(0.1) await client.close() raise SkipTest("Elasticsearch failed to start.") @@ -110,7 +114,7 @@ def _get_version(version_string: str) -> Tuple[int, ...]: return tuple(int(v) if v.isdigit() else 999 for v in version) -@fixture +@pytest.fixture def client(elasticsearch_url) -> Elasticsearch: try: connection = get_test_client( @@ -121,10 +125,10 @@ def client(elasticsearch_url) -> Elasticsearch: wipe_cluster(connection) connection.close() except SkipTest: - skip() + pytest.skip() -@pytest_asyncio.fixture +@pytest.fixture async def async_client(elasticsearch_url) -> AsyncGenerator[AsyncElasticsearch, None]: try: connection = await get_async_test_client( @@ -135,10 +139,10 @@ async def async_client(elasticsearch_url) -> AsyncGenerator[AsyncElasticsearch, wipe_cluster(connection) await connection.close() except SkipTest: - skip() + pytest.skip() -@fixture +@pytest.fixture def es_version(client: Elasticsearch) -> Generator[Tuple[int, ...], None, None]: info = client.info() yield tuple( @@ -147,7 +151,7 @@ def es_version(client: Elasticsearch) -> Generator[Tuple[int, ...], None, None]: ) -@fixture +@pytest.fixture def write_client(client: Elasticsearch) -> Generator[Elasticsearch, None, None]: yield client for index_name in client.indices.get(index="test-*", expand_wildcards="all"): @@ -158,14 +162,14 @@ def write_client(client: Elasticsearch) -> Generator[Elasticsearch, None, None]: ) -@pytest_asyncio.fixture +@pytest.fixture async def async_write_client( write_client: Elasticsearch, async_client: AsyncElasticsearch ) -> AsyncGenerator[AsyncElasticsearch, None]: yield async_client -@fixture +@pytest.fixture def mock_client( dummy_response: ObjectApiResponse[Any], ) -> Generator[Elasticsearch, None, None]: @@ -179,7 +183,7 @@ def mock_client( connections._kwargs = {} -@fixture +@pytest.fixture def async_mock_client( dummy_response: ObjectApiResponse[Any], ) -> Generator[Elasticsearch, None, None]: @@ -195,7 +199,7 @@ def async_mock_client( async_connections._kwargs = {} -@fixture +@pytest.fixture def data_client(client: Elasticsearch) -> Generator[Elasticsearch, None, None]: # create mappings create_git_index(client, "git") @@ -208,14 +212,14 @@ def data_client(client: Elasticsearch) -> Generator[Elasticsearch, None, None]: client.options(ignore_status=404).indices.delete(index="flat-git") -@pytest_asyncio.fixture +@pytest.fixture async def async_data_client( data_client: Elasticsearch, async_client: AsyncElasticsearch ) -> AsyncGenerator[AsyncElasticsearch, None]: yield async_client -@fixture +@pytest.fixture def dummy_response() -> ObjectApiResponse[Any]: return ObjectApiResponse( meta=None, @@ -271,7 +275,7 @@ def dummy_response() -> ObjectApiResponse[Any]: ) -@fixture +@pytest.fixture def aggs_search() -> Search: s = Search(index="flat-git") s.aggs.bucket("popular_files", "terms", field="files", size=2).metric( @@ -284,7 +288,7 @@ def aggs_search() -> Search: return s -@fixture +@pytest.fixture def aggs_data() -> Dict[str, Any]: return { "took": 4, @@ -440,7 +444,7 @@ def make_pr(pr_module: Any) -> Any: ) -@fixture +@pytest.fixture def pull_request(write_client: Elasticsearch) -> sync_document.PullRequest: sync_document.PullRequest.init() pr = cast(sync_document.PullRequest, make_pr(sync_document)) @@ -448,7 +452,7 @@ def pull_request(write_client: Elasticsearch) -> sync_document.PullRequest: return pr -@pytest_asyncio.fixture +@pytest.fixture async def async_pull_request( async_write_client: AsyncElasticsearch, ) -> async_document.PullRequest: @@ -458,7 +462,7 @@ async def async_pull_request( return pr -@fixture +@pytest.fixture def setup_ubq_tests(client: Elasticsearch) -> str: index = "test-git" create_git_index(client, index) diff --git a/test_elasticsearch/test_dsl/test_integration/_async/test_analysis.py b/test_elasticsearch/test_dsl/test_integration/_async/test_analysis.py index 00598d4d5..c661359c0 100644 --- a/test_elasticsearch/test_dsl/test_integration/_async/test_analysis.py +++ b/test_elasticsearch/test_dsl/test_integration/_async/test_analysis.py @@ -21,7 +21,7 @@ from elasticsearch.dsl import analyzer, token_filter, tokenizer -@pytest.mark.asyncio +@pytest.mark.anyio async def test_simulate_with_just__builtin_tokenizer( async_client: AsyncElasticsearch, ) -> None: @@ -32,7 +32,7 @@ async def test_simulate_with_just__builtin_tokenizer( assert tokens[0].token == "Hello World!" -@pytest.mark.asyncio +@pytest.mark.anyio async def test_simulate_complex(async_client: AsyncElasticsearch) -> None: a = analyzer( "my-analyzer", @@ -46,7 +46,7 @@ async def test_simulate_complex(async_client: AsyncElasticsearch) -> None: assert ["this", "works"] == [t.token for t in tokens] -@pytest.mark.asyncio +@pytest.mark.anyio async def test_simulate_builtin(async_client: AsyncElasticsearch) -> None: a = analyzer("my-analyzer", "english") tokens = (await a.async_simulate("fixes running")).tokens diff --git a/test_elasticsearch/test_dsl/test_integration/_async/test_document.py b/test_elasticsearch/test_dsl/test_integration/_async/test_document.py index 36f055583..274db1027 100644 --- a/test_elasticsearch/test_dsl/test_integration/_async/test_document.py +++ b/test_elasticsearch/test_dsl/test_integration/_async/test_document.py @@ -142,7 +142,7 @@ class Index: name = "tags" -@pytest.mark.asyncio +@pytest.mark.anyio async def test_serialization(async_write_client: AsyncElasticsearch) -> None: await SerializationDoc.init() await async_write_client.index( @@ -174,7 +174,7 @@ async def test_serialization(async_write_client: AsyncElasticsearch) -> None: } -@pytest.mark.asyncio +@pytest.mark.anyio async def test_nested_inner_hits_are_wrapped_properly(async_pull_request: Any) -> None: history_query = Q( "nested", @@ -203,7 +203,7 @@ async def test_nested_inner_hits_are_wrapped_properly(async_pull_request: Any) - assert "score" in history.meta -@pytest.mark.asyncio +@pytest.mark.anyio async def test_nested_inner_hits_are_deserialized_properly( async_pull_request: Any, ) -> None: @@ -221,7 +221,7 @@ async def test_nested_inner_hits_are_deserialized_properly( assert isinstance(pr.comments[0].created_at, datetime) -@pytest.mark.asyncio +@pytest.mark.anyio async def test_nested_top_hits_are_wrapped_properly(async_pull_request: Any) -> None: s = PullRequest.search() s.aggs.bucket("comments", "nested", path="comments").metric( @@ -234,7 +234,7 @@ async def test_nested_top_hits_are_wrapped_properly(async_pull_request: Any) -> assert isinstance(r.aggregations.comments.hits.hits[0], Comment) -@pytest.mark.asyncio +@pytest.mark.anyio async def test_update_object_field(async_write_client: AsyncElasticsearch) -> None: await Wiki.init() w = Wiki( @@ -255,7 +255,7 @@ async def test_update_object_field(async_write_client: AsyncElasticsearch) -> No assert w.ranked == {"test1": 0.1, "topic2": 0.2} -@pytest.mark.asyncio +@pytest.mark.anyio async def test_update_script(async_write_client: AsyncElasticsearch) -> None: await Wiki.init() w = Wiki(owner=User(name="Honza Kral"), _id="elasticsearch-py", views=42) @@ -266,7 +266,7 @@ async def test_update_script(async_write_client: AsyncElasticsearch) -> None: assert w.views == 47 -@pytest.mark.asyncio +@pytest.mark.anyio async def test_update_script_with_dict(async_write_client: AsyncElasticsearch) -> None: await Wiki.init() w = Wiki(owner=User(name="Honza Kral"), _id="elasticsearch-py", views=42) @@ -284,7 +284,7 @@ async def test_update_script_with_dict(async_write_client: AsyncElasticsearch) - assert w.views == 47 -@pytest.mark.asyncio +@pytest.mark.anyio async def test_update_retry_on_conflict(async_write_client: AsyncElasticsearch) -> None: await Wiki.init() w = Wiki(owner=User(name="Honza Kral"), _id="elasticsearch-py", views=42) @@ -306,7 +306,7 @@ async def test_update_retry_on_conflict(async_write_client: AsyncElasticsearch) assert w.views == 52 -@pytest.mark.asyncio +@pytest.mark.anyio @pytest.mark.parametrize("retry_on_conflict", [None, 0]) async def test_update_conflicting_version( async_write_client: AsyncElasticsearch, retry_on_conflict: bool @@ -330,7 +330,7 @@ async def test_update_conflicting_version( ) -@pytest.mark.asyncio +@pytest.mark.anyio async def test_save_and_update_return_doc_meta( async_write_client: AsyncElasticsearch, ) -> None: @@ -365,14 +365,14 @@ async def test_save_and_update_return_doc_meta( } -@pytest.mark.asyncio +@pytest.mark.anyio async def test_init(async_write_client: AsyncElasticsearch) -> None: await Repository.init(index="test-git") assert await async_write_client.indices.exists(index="test-git") -@pytest.mark.asyncio +@pytest.mark.anyio async def test_get_raises_404_on_index_missing( async_data_client: AsyncElasticsearch, ) -> None: @@ -380,7 +380,7 @@ async def test_get_raises_404_on_index_missing( await Repository.get("elasticsearch-dsl-php", index="not-there") -@pytest.mark.asyncio +@pytest.mark.anyio async def test_get_raises_404_on_non_existent_id( async_data_client: AsyncElasticsearch, ) -> None: @@ -388,7 +388,7 @@ async def test_get_raises_404_on_non_existent_id( await Repository.get("elasticsearch-dsl-php") -@pytest.mark.asyncio +@pytest.mark.anyio async def test_get_returns_none_if_404_ignored( async_data_client: AsyncElasticsearch, ) -> None: @@ -397,7 +397,7 @@ async def test_get_returns_none_if_404_ignored( ) -@pytest.mark.asyncio +@pytest.mark.anyio async def test_get_returns_none_if_404_ignored_and_index_doesnt_exist( async_data_client: AsyncElasticsearch, ) -> None: @@ -406,7 +406,7 @@ async def test_get_returns_none_if_404_ignored_and_index_doesnt_exist( ) -@pytest.mark.asyncio +@pytest.mark.anyio async def test_get(async_data_client: AsyncElasticsearch) -> None: elasticsearch_repo = await Repository.get("elasticsearch-dsl-py") @@ -415,17 +415,17 @@ async def test_get(async_data_client: AsyncElasticsearch) -> None: assert datetime(2014, 3, 3) == elasticsearch_repo.created_at -@pytest.mark.asyncio +@pytest.mark.anyio async def test_exists_return_true(async_data_client: AsyncElasticsearch) -> None: assert await Repository.exists("elasticsearch-dsl-py") -@pytest.mark.asyncio +@pytest.mark.anyio async def test_exists_false(async_data_client: AsyncElasticsearch) -> None: assert not await Repository.exists("elasticsearch-dsl-php") -@pytest.mark.asyncio +@pytest.mark.anyio async def test_get_with_tz_date(async_data_client: AsyncElasticsearch) -> None: first_commit = await Commit.get( id="3ca6e1e73a071a705b4babd2f581c91a2a3e5037", routing="elasticsearch-dsl-py" @@ -439,7 +439,7 @@ async def test_get_with_tz_date(async_data_client: AsyncElasticsearch) -> None: ) -@pytest.mark.asyncio +@pytest.mark.anyio async def test_save_with_tz_date(async_data_client: AsyncElasticsearch) -> None: tzinfo = timezone("Europe/Prague") first_commit = await Commit.get( @@ -471,7 +471,7 @@ async def test_save_with_tz_date(async_data_client: AsyncElasticsearch) -> None: ] -@pytest.mark.asyncio +@pytest.mark.anyio async def test_mget(async_data_client: AsyncElasticsearch) -> None: commits = await Commit.mget(COMMIT_DOCS_WITH_MISSING) assert commits[0] is None @@ -482,7 +482,7 @@ async def test_mget(async_data_client: AsyncElasticsearch) -> None: assert commits[3].meta.id == "eb3e543323f189fd7b698e66295427204fff5755" -@pytest.mark.asyncio +@pytest.mark.anyio async def test_mget_raises_exception_when_missing_param_is_invalid( async_data_client: AsyncElasticsearch, ) -> None: @@ -490,7 +490,7 @@ async def test_mget_raises_exception_when_missing_param_is_invalid( await Commit.mget(COMMIT_DOCS_WITH_MISSING, missing="raj") -@pytest.mark.asyncio +@pytest.mark.anyio async def test_mget_raises_404_when_missing_param_is_raise( async_data_client: AsyncElasticsearch, ) -> None: @@ -498,7 +498,7 @@ async def test_mget_raises_404_when_missing_param_is_raise( await Commit.mget(COMMIT_DOCS_WITH_MISSING, missing="raise") -@pytest.mark.asyncio +@pytest.mark.anyio async def test_mget_ignores_missing_docs_when_missing_param_is_skip( async_data_client: AsyncElasticsearch, ) -> None: @@ -509,7 +509,7 @@ async def test_mget_ignores_missing_docs_when_missing_param_is_skip( assert commits[1].meta.id == "eb3e543323f189fd7b698e66295427204fff5755" -@pytest.mark.asyncio +@pytest.mark.anyio async def test_update_works_from_search_response( async_data_client: AsyncElasticsearch, ) -> None: @@ -524,7 +524,7 @@ async def test_update_works_from_search_response( assert "elasticsearch" == new_version.owner.name -@pytest.mark.asyncio +@pytest.mark.anyio async def test_update(async_data_client: AsyncElasticsearch) -> None: elasticsearch_repo = await Repository.get("elasticsearch-dsl-py") assert elasticsearch_repo is not None @@ -551,7 +551,7 @@ async def test_update(async_data_client: AsyncElasticsearch) -> None: assert "primary_term" in new_version.meta -@pytest.mark.asyncio +@pytest.mark.anyio async def test_save_updates_existing_doc(async_data_client: AsyncElasticsearch) -> None: elasticsearch_repo = await Repository.get("elasticsearch-dsl-py") assert elasticsearch_repo is not None @@ -566,7 +566,7 @@ async def test_save_updates_existing_doc(async_data_client: AsyncElasticsearch) assert new_repo["_seq_no"] == elasticsearch_repo.meta.seq_no -@pytest.mark.asyncio +@pytest.mark.anyio async def test_update_empty_field(async_client: AsyncElasticsearch) -> None: await Tags._index.delete(ignore_unavailable=True) await Tags.init() @@ -579,7 +579,7 @@ async def test_update_empty_field(async_client: AsyncElasticsearch) -> None: assert r.hits[0].tags == [] -@pytest.mark.asyncio +@pytest.mark.anyio async def test_save_automatically_uses_seq_no_and_primary_term( async_data_client: AsyncElasticsearch, ) -> None: @@ -591,7 +591,7 @@ async def test_save_automatically_uses_seq_no_and_primary_term( await elasticsearch_repo.save() -@pytest.mark.asyncio +@pytest.mark.anyio async def test_delete_automatically_uses_seq_no_and_primary_term( async_data_client: AsyncElasticsearch, ) -> None: @@ -609,7 +609,7 @@ def assert_doc_equals(expected: Any, actual: Any) -> None: assert actual[f] == expected[f] -@pytest.mark.asyncio +@pytest.mark.anyio async def test_can_save_to_different_index( async_write_client: AsyncElasticsearch, ) -> None: @@ -627,7 +627,7 @@ async def test_can_save_to_different_index( ) -@pytest.mark.asyncio +@pytest.mark.anyio @pytest.mark.parametrize("validate", (True, False)) async def test_save_without_skip_empty_will_include_empty_fields( async_write_client: AsyncElasticsearch, @@ -673,7 +673,7 @@ async def test_save_without_skip_empty_will_include_empty_fields( ) -@pytest.mark.asyncio +@pytest.mark.anyio async def test_delete(async_write_client: AsyncElasticsearch) -> None: await async_write_client.create( index="test-document", @@ -695,12 +695,12 @@ async def test_delete(async_write_client: AsyncElasticsearch) -> None: ) -@pytest.mark.asyncio +@pytest.mark.anyio async def test_search(async_data_client: AsyncElasticsearch) -> None: assert await Repository.search().count() == 1 -@pytest.mark.asyncio +@pytest.mark.anyio async def test_search_returns_proper_doc_classes( async_data_client: AsyncElasticsearch, ) -> None: @@ -712,7 +712,7 @@ async def test_search_returns_proper_doc_classes( assert elasticsearch_repo.owner.name == "elasticsearch" -@pytest.mark.asyncio +@pytest.mark.anyio async def test_refresh_mapping(async_data_client: AsyncElasticsearch) -> None: class Commit(AsyncDocument): class Index: @@ -727,7 +727,7 @@ class Index: assert isinstance(Commit._index._mapping["committed_date"], Date) -@pytest.mark.asyncio +@pytest.mark.anyio async def test_highlight_in_meta(async_data_client: AsyncElasticsearch) -> None: commit = ( await Commit.search() @@ -742,7 +742,7 @@ async def test_highlight_in_meta(async_data_client: AsyncElasticsearch) -> None: assert len(commit.meta.highlight["description"]) > 0 -@pytest.mark.asyncio +@pytest.mark.anyio async def test_bulk(async_data_client: AsyncElasticsearch) -> None: class Address(InnerDoc): street: str @@ -830,7 +830,7 @@ async def gen3() -> AsyncIterator[Union[Doc, Dict[str, Any]]]: } -@pytest.mark.asyncio +@pytest.mark.anyio async def test_legacy_dense_vector( async_client: AsyncElasticsearch, es_version: Tuple[int, ...] ) -> None: @@ -854,7 +854,7 @@ class Index: assert docs[0].float_vector == doc.float_vector -@pytest.mark.asyncio +@pytest.mark.anyio async def test_dense_vector( async_client: AsyncElasticsearch, es_version: Tuple[int, ...] ) -> None: @@ -884,7 +884,7 @@ class Index: assert docs[0].bit_vector == doc.bit_vector -@pytest.mark.asyncio +@pytest.mark.anyio async def test_copy_to(async_client: AsyncElasticsearch) -> None: class Person(AsyncDocument): first_name: M[str] = mapped_field(Text(copy_to=["full_name", "all"])) diff --git a/test_elasticsearch/test_dsl/test_integration/_async/test_esql.py b/test_elasticsearch/test_dsl/test_integration/_async/test_esql.py index ae99873f8..1229ece20 100644 --- a/test_elasticsearch/test_dsl/test_integration/_async/test_esql.py +++ b/test_elasticsearch/test_dsl/test_integration/_async/test_esql.py @@ -138,7 +138,7 @@ async def load_db(): await Employee._index.refresh() -@pytest.mark.asyncio +@pytest.mark.anyio async def test_esql(async_client): await load_db() @@ -184,7 +184,7 @@ async def test_esql(async_client): assert r.body["values"] == [["Luna"], ["Cannon"]] -@pytest.mark.asyncio +@pytest.mark.anyio async def test_esql_dsl(async_client): await load_db() diff --git a/test_elasticsearch/test_dsl/test_integration/_async/test_faceted_search.py b/test_elasticsearch/test_dsl/test_integration/_async/test_faceted_search.py index bb0fd9257..82f1a8767 100644 --- a/test_elasticsearch/test_dsl/test_integration/_async/test_faceted_search.py +++ b/test_elasticsearch/test_dsl/test_integration/_async/test_faceted_search.py @@ -126,7 +126,7 @@ class PRSearch(AsyncFacetedSearch): return PRSearch -@pytest.mark.asyncio +@pytest.mark.anyio async def test_facet_with_custom_metric(async_data_client: AsyncElasticsearch) -> None: ms = MetricSearch() r = await ms.execute() @@ -136,7 +136,7 @@ async def test_facet_with_custom_metric(async_data_client: AsyncElasticsearch) - assert dates[0] == 1399038439000 -@pytest.mark.asyncio +@pytest.mark.anyio async def test_nested_facet( async_pull_request: PullRequest, pr_search_cls: Type[AsyncFacetedSearch] ) -> None: @@ -147,7 +147,7 @@ async def test_nested_facet( assert [(datetime(2018, 1, 1, 0, 0), 1, False)] == r.facets.comments -@pytest.mark.asyncio +@pytest.mark.anyio async def test_nested_facet_with_filter( async_pull_request: PullRequest, pr_search_cls: Type[AsyncFacetedSearch] ) -> None: @@ -162,7 +162,7 @@ async def test_nested_facet_with_filter( assert not r.hits -@pytest.mark.asyncio +@pytest.mark.anyio async def test_datehistogram_facet( async_data_client: AsyncElasticsearch, repo_search_cls: Type[AsyncFacetedSearch] ) -> None: @@ -173,7 +173,7 @@ async def test_datehistogram_facet( assert [(datetime(2014, 3, 1, 0, 0), 1, False)] == r.facets.created -@pytest.mark.asyncio +@pytest.mark.anyio async def test_boolean_facet( async_data_client: AsyncElasticsearch, repo_search_cls: Type[AsyncFacetedSearch] ) -> None: @@ -186,7 +186,7 @@ async def test_boolean_facet( assert value is True -@pytest.mark.asyncio +@pytest.mark.anyio async def test_empty_search_finds_everything( async_data_client: AsyncElasticsearch, es_version: Tuple[int, ...], @@ -236,7 +236,7 @@ async def test_empty_search_finds_everything( ] == r.facets.deletions -@pytest.mark.asyncio +@pytest.mark.anyio async def test_term_filters_are_shown_as_selected_and_data_is_filtered( async_data_client: AsyncElasticsearch, commit_search_cls: Type[AsyncFacetedSearch] ) -> None: @@ -283,7 +283,7 @@ async def test_term_filters_are_shown_as_selected_and_data_is_filtered( ] == r.facets.deletions -@pytest.mark.asyncio +@pytest.mark.anyio async def test_range_filters_are_shown_as_selected_and_data_is_filtered( async_data_client: AsyncElasticsearch, commit_search_cls: Type[AsyncFacetedSearch] ) -> None: @@ -294,7 +294,7 @@ async def test_range_filters_are_shown_as_selected_and_data_is_filtered( assert 19 == r.hits.total.value # type: ignore[attr-defined] -@pytest.mark.asyncio +@pytest.mark.anyio async def test_pagination( async_data_client: AsyncElasticsearch, commit_search_cls: Type[AsyncFacetedSearch] ) -> None: diff --git a/test_elasticsearch/test_dsl/test_integration/_async/test_index.py b/test_elasticsearch/test_dsl/test_integration/_async/test_index.py index e150d1e59..3e95e89e3 100644 --- a/test_elasticsearch/test_dsl/test_integration/_async/test_index.py +++ b/test_elasticsearch/test_dsl/test_integration/_async/test_index.py @@ -34,7 +34,7 @@ class Post(AsyncDocument): published_from = Date() -@pytest.mark.asyncio +@pytest.mark.anyio async def test_index_template_works(async_write_client: AsyncElasticsearch) -> None: it = AsyncIndexTemplate("test-template", "test-legacy-*") it.document(Post) @@ -56,7 +56,7 @@ async def test_index_template_works(async_write_client: AsyncElasticsearch) -> N } == await async_write_client.indices.get_mapping(index="test-legacy-blog") -@pytest.mark.asyncio +@pytest.mark.anyio async def test_composable_index_template_works( async_write_client: AsyncElasticsearch, ) -> None: @@ -80,7 +80,7 @@ async def test_composable_index_template_works( } == await async_write_client.indices.get_mapping(index="test-blog") -@pytest.mark.asyncio +@pytest.mark.anyio async def test_index_can_be_saved_even_with_settings( async_write_client: AsyncElasticsearch, ) -> None: @@ -98,13 +98,13 @@ async def test_index_can_be_saved_even_with_settings( ) -@pytest.mark.asyncio +@pytest.mark.anyio async def test_index_exists(async_data_client: AsyncElasticsearch) -> None: assert await AsyncIndex("git").exists() assert not await AsyncIndex("not-there").exists() -@pytest.mark.asyncio +@pytest.mark.anyio async def test_index_can_be_created_with_settings_and_mappings( async_write_client: AsyncElasticsearch, ) -> None: @@ -132,7 +132,7 @@ async def test_index_can_be_created_with_settings_and_mappings( } -@pytest.mark.asyncio +@pytest.mark.anyio async def test_delete(async_write_client: AsyncElasticsearch) -> None: await async_write_client.indices.create( index="test-index", @@ -144,7 +144,7 @@ async def test_delete(async_write_client: AsyncElasticsearch) -> None: assert not await async_write_client.indices.exists(index="test-index") -@pytest.mark.asyncio +@pytest.mark.anyio async def test_multiple_indices_with_same_doc_type_work( async_write_client: AsyncElasticsearch, ) -> None: diff --git a/test_elasticsearch/test_dsl/test_integration/_async/test_mapping.py b/test_elasticsearch/test_dsl/test_integration/_async/test_mapping.py index f370c89c4..c7aa02a67 100644 --- a/test_elasticsearch/test_dsl/test_integration/_async/test_mapping.py +++ b/test_elasticsearch/test_dsl/test_integration/_async/test_mapping.py @@ -22,7 +22,7 @@ from elasticsearch.dsl import AsyncMapping, analysis, exceptions -@pytest.mark.asyncio +@pytest.mark.anyio async def test_mapping_saved_into_es(async_write_client: AsyncElasticsearch) -> None: m = AsyncMapping() m.field( @@ -43,7 +43,7 @@ async def test_mapping_saved_into_es(async_write_client: AsyncElasticsearch) -> } == await async_write_client.indices.get_mapping(index="test-mapping") -@pytest.mark.asyncio +@pytest.mark.anyio async def test_mapping_saved_into_es_when_index_already_exists_closed( async_write_client: AsyncElasticsearch, ) -> None: @@ -71,7 +71,7 @@ async def test_mapping_saved_into_es_when_index_already_exists_closed( } == await async_write_client.indices.get_mapping(index="test-mapping") -@pytest.mark.asyncio +@pytest.mark.anyio async def test_mapping_saved_into_es_when_index_already_exists_with_analysis( async_write_client: AsyncElasticsearch, ) -> None: @@ -103,7 +103,7 @@ async def test_mapping_saved_into_es_when_index_already_exists_with_analysis( } == await async_write_client.indices.get_mapping(index="test-mapping") -@pytest.mark.asyncio +@pytest.mark.anyio async def test_mapping_gets_updated_from_es( async_write_client: AsyncElasticsearch, ) -> None: diff --git a/test_elasticsearch/test_dsl/test_integration/_async/test_search.py b/test_elasticsearch/test_dsl/test_integration/_async/test_search.py index a63f6746a..2499b8d03 100644 --- a/test_elasticsearch/test_dsl/test_integration/_async/test_search.py +++ b/test_elasticsearch/test_dsl/test_integration/_async/test_search.py @@ -52,7 +52,7 @@ class Index: name = "flat-git" -@pytest.mark.asyncio +@pytest.mark.anyio async def test_filters_aggregation_buckets_are_accessible( async_data_client: AsyncElasticsearch, ) -> None: @@ -77,7 +77,7 @@ async def test_filters_aggregation_buckets_are_accessible( ) -@pytest.mark.asyncio +@pytest.mark.anyio async def test_top_hits_are_wrapped_in_response( async_data_client: AsyncElasticsearch, ) -> None: @@ -96,7 +96,7 @@ async def test_top_hits_are_wrapped_in_response( assert isinstance(hits[0], Commit) -@pytest.mark.asyncio +@pytest.mark.anyio async def test_inner_hits_are_wrapped_in_response( async_data_client: AsyncElasticsearch, ) -> None: @@ -112,7 +112,7 @@ async def test_inner_hits_are_wrapped_in_response( ) -@pytest.mark.asyncio +@pytest.mark.anyio async def test_inner_hits_are_serialized_to_dict( async_data_client: AsyncElasticsearch, ) -> None: @@ -133,7 +133,7 @@ async def test_inner_hits_are_serialized_to_dict( assert isinstance(d["hits"]["hits"][0]["inner_hits"]["repo"], dict) -@pytest.mark.asyncio +@pytest.mark.anyio async def test_scan_respects_doc_types(async_data_client: AsyncElasticsearch) -> None: repos = [repo async for repo in Repository.search().scan()] @@ -142,7 +142,7 @@ async def test_scan_respects_doc_types(async_data_client: AsyncElasticsearch) -> assert repos[0].organization == "elasticsearch" -@pytest.mark.asyncio +@pytest.mark.anyio async def test_scan_iterates_through_all_docs( async_data_client: AsyncElasticsearch, ) -> None: @@ -154,7 +154,7 @@ async def test_scan_iterates_through_all_docs( assert {d["_id"] for d in FLAT_DATA} == {c.meta.id for c in commits} -@pytest.mark.asyncio +@pytest.mark.anyio async def test_search_after(async_data_client: AsyncElasticsearch) -> None: page_size = 7 s = AsyncSearch(index="flat-git")[:page_size].sort("authored_date") @@ -170,7 +170,7 @@ async def test_search_after(async_data_client: AsyncElasticsearch) -> None: assert {d["_id"] for d in FLAT_DATA} == {c.meta.id for c in commits} -@pytest.mark.asyncio +@pytest.mark.anyio async def test_search_after_no_search(async_data_client: AsyncElasticsearch) -> None: s = AsyncSearch(index="flat-git") with raises( @@ -184,7 +184,7 @@ async def test_search_after_no_search(async_data_client: AsyncElasticsearch) -> s.search_after() -@pytest.mark.asyncio +@pytest.mark.anyio async def test_search_after_no_sort(async_data_client: AsyncElasticsearch) -> None: s = AsyncSearch(index="flat-git") r = await s.execute() @@ -194,7 +194,7 @@ async def test_search_after_no_sort(async_data_client: AsyncElasticsearch) -> No r.search_after() -@pytest.mark.asyncio +@pytest.mark.anyio async def test_search_after_no_results(async_data_client: AsyncElasticsearch) -> None: s = AsyncSearch(index="flat-git")[:100].sort("authored_date") r = await s.execute() @@ -208,7 +208,7 @@ async def test_search_after_no_results(async_data_client: AsyncElasticsearch) -> r.search_after() -@pytest.mark.asyncio +@pytest.mark.anyio async def test_point_in_time(async_data_client: AsyncElasticsearch) -> None: page_size = 7 commits = [] @@ -229,7 +229,7 @@ async def test_point_in_time(async_data_client: AsyncElasticsearch) -> None: assert {d["_id"] for d in FLAT_DATA} == {c.meta.id for c in commits} -@pytest.mark.asyncio +@pytest.mark.anyio async def test_iterate(async_data_client: AsyncElasticsearch) -> None: s = AsyncSearch(index="flat-git") @@ -239,7 +239,7 @@ async def test_iterate(async_data_client: AsyncElasticsearch) -> None: assert {d["_id"] for d in FLAT_DATA} == {c.meta.id for c in commits} -@pytest.mark.asyncio +@pytest.mark.anyio async def test_response_is_cached(async_data_client: AsyncElasticsearch) -> None: s = Repository.search() repos = [repo async for repo in s] @@ -248,7 +248,7 @@ async def test_response_is_cached(async_data_client: AsyncElasticsearch) -> None assert s._response.hits == repos -@pytest.mark.asyncio +@pytest.mark.anyio async def test_multi_search(async_data_client: AsyncElasticsearch) -> None: s1 = Repository.search() s2 = AsyncSearch[Repository](index="flat-git") @@ -266,7 +266,7 @@ async def test_multi_search(async_data_client: AsyncElasticsearch) -> None: assert r2._search is s2 -@pytest.mark.asyncio +@pytest.mark.anyio async def test_multi_missing(async_data_client: AsyncElasticsearch) -> None: s1 = Repository.search() s2 = AsyncSearch[Repository](index="flat-git") @@ -290,7 +290,7 @@ async def test_multi_missing(async_data_client: AsyncElasticsearch) -> None: assert r3 is None -@pytest.mark.asyncio +@pytest.mark.anyio async def test_raw_subfield_can_be_used_in_aggs( async_data_client: AsyncElasticsearch, ) -> None: diff --git a/test_elasticsearch/test_dsl/test_integration/_async/test_update_by_query.py b/test_elasticsearch/test_dsl/test_integration/_async/test_update_by_query.py index b051d284a..880bb9482 100644 --- a/test_elasticsearch/test_dsl/test_integration/_async/test_update_by_query.py +++ b/test_elasticsearch/test_dsl/test_integration/_async/test_update_by_query.py @@ -22,7 +22,7 @@ from elasticsearch.dsl.search import Q -@pytest.mark.asyncio +@pytest.mark.anyio async def test_update_by_query_no_script( async_write_client: AsyncElasticsearch, setup_ubq_tests: str ) -> None: @@ -44,7 +44,7 @@ async def test_update_by_query_no_script( assert response.success() -@pytest.mark.asyncio +@pytest.mark.anyio async def test_update_by_query_with_script( async_write_client: AsyncElasticsearch, setup_ubq_tests: str ) -> None: @@ -64,7 +64,7 @@ async def test_update_by_query_with_script( assert response.version_conflicts == 0 -@pytest.mark.asyncio +@pytest.mark.anyio async def test_delete_by_query_with_script( async_write_client: AsyncElasticsearch, setup_ubq_tests: str ) -> None: diff --git a/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_alias_migration.py b/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_alias_migration.py index d2b4294a4..ea2db3362 100644 --- a/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_alias_migration.py +++ b/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_alias_migration.py @@ -23,7 +23,7 @@ from ..async_examples.alias_migration import ALIAS, PATTERN, BlogPost, migrate -@pytest.mark.asyncio +@pytest.mark.anyio async def test_alias_migration(async_write_client: AsyncElasticsearch) -> None: # create the index await alias_migration.setup() diff --git a/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_completion.py b/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_completion.py index 13e73e14a..cb923e474 100644 --- a/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_completion.py +++ b/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_completion.py @@ -22,7 +22,7 @@ from ..async_examples.completion import Person -@pytest.mark.asyncio +@pytest.mark.anyio async def test_person_suggests_on_all_variants_of_name( async_write_client: AsyncElasticsearch, ) -> None: diff --git a/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_composite_aggs.py b/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_composite_aggs.py index 2d3ab2df7..5110bbd50 100644 --- a/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_composite_aggs.py +++ b/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_composite_aggs.py @@ -23,7 +23,7 @@ from ..async_examples.composite_agg import scan_aggs -@pytest.mark.asyncio +@pytest.mark.anyio async def test_scan_aggs_exhausts_all_files( async_data_client: AsyncElasticsearch, ) -> None: @@ -34,7 +34,7 @@ async def test_scan_aggs_exhausts_all_files( assert len(file_list) == 26 -@pytest.mark.asyncio +@pytest.mark.anyio async def test_scan_aggs_with_multiple_aggs( async_data_client: AsyncElasticsearch, ) -> None: diff --git a/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_parent_child.py b/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_parent_child.py index a730c8839..877c26fd9 100644 --- a/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_parent_child.py +++ b/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_parent_child.py @@ -18,7 +18,6 @@ from datetime import datetime import pytest -import pytest_asyncio from elasticsearch import AsyncElasticsearch from elasticsearch.dsl import Q @@ -42,7 +41,7 @@ ) -@pytest_asyncio.fixture +@pytest.fixture async def question(async_write_client: AsyncElasticsearch) -> Question: await setup() assert await async_write_client.indices.exists_index_template(name="base") @@ -64,7 +63,7 @@ async def question(async_write_client: AsyncElasticsearch) -> Question: return q -@pytest.mark.asyncio +@pytest.mark.anyio async def test_comment( async_write_client: AsyncElasticsearch, question: Question ) -> None: @@ -79,7 +78,7 @@ async def test_comment( assert c.author.username == "fxdgear" -@pytest.mark.asyncio +@pytest.mark.anyio async def test_question_answer( async_write_client: AsyncElasticsearch, question: Question ) -> None: diff --git a/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_percolate.py b/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_percolate.py index cf1721b8e..3b61c96e5 100644 --- a/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_percolate.py +++ b/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_percolate.py @@ -22,7 +22,7 @@ from ..async_examples.percolate import BlogPost, setup -@pytest.mark.asyncio +@pytest.mark.anyio async def test_post_gets_tagged_automatically( async_write_client: AsyncElasticsearch, ) -> None: diff --git a/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_vectors.py b/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_vectors.py index 3af9a877f..347339808 100644 --- a/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_vectors.py +++ b/test_elasticsearch/test_dsl/test_integration/test_examples/_async/test_vectors.py @@ -26,7 +26,7 @@ from elasticsearch import AsyncElasticsearch -@pytest.mark.asyncio +@pytest.mark.anyio async def test_vector_search( async_write_client: AsyncElasticsearch, es_version: Tuple[int, ...] ) -> None: diff --git a/test_elasticsearch/test_server/test_rest_api_spec.py b/test_elasticsearch/test_server/test_rest_api_spec.py index 768453c10..4e3ff74a9 100644 --- a/test_elasticsearch/test_server/test_rest_api_spec.py +++ b/test_elasticsearch/test_server/test_rest_api_spec.py @@ -20,6 +20,7 @@ some integration tests. These files are shared among all official Elasticsearch clients. """ +import copy import io import json import os @@ -117,6 +118,7 @@ def __init__(self, client): self._state = {} def use_spec(self, test_spec): + test_spec = copy.deepcopy(test_spec) self._setup_code = test_spec.pop("setup", None) self._run_code = test_spec.pop("run", None) self._teardown_code = test_spec.pop("teardown", None) diff --git a/utils/run-unasync-dsl.py b/utils/run-unasync-dsl.py index f9035de76..2d4e0f089 100644 --- a/utils/run-unasync-dsl.py +++ b/utils/run-unasync-dsl.py @@ -74,7 +74,6 @@ def main(check=False): "async_examples": "examples", "async_sleep": "sleep", "assert_awaited_once_with": "assert_called_once_with", - "pytest_asyncio": "pytest", "asynccontextmanager": "contextmanager", } rules = [ @@ -125,15 +124,16 @@ def main(check=False): f"{output_dir}{file}", ] ) - subprocess.check_call( - [ - "sed", - "-i.bak", - "s/pytest.mark.asyncio/pytest.mark.sync/", - f"{output_dir}{file}", - ] - ) - subprocess.check_call(["rm", f"{output_dir}{file}.bak"]) + for library in ["asyncio", "trio", "anyio"]: + subprocess.check_call( + [ + "sed", + "-i.bak", + f"s/pytest.mark.{library}/pytest.mark.sync/", + f"{output_dir}{file}", + ] + ) + subprocess.check_call(["rm", f"{output_dir}{file}.bak"]) if check: # make sure there are no differences between _sync and _sync_check From 16ae9f52e8fd181e5487220f520ac1ee1ab4bd3c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 14:46:50 +0100 Subject: [PATCH 09/36] Pydantic integration (#3086) (#3120) * Support `Annotated` typing hint * Add option to exclude DSL class field from mapping * Pydantic integration with the BaseESModel class * object and nested fields * complete CRUD example * Use a smaller dataset * documentation * Update examples/quotes/backend/pyproject.toml * Update examples/quotes/backend/quotes.py * Use a better screenshot --------- (cherry picked from commit f842d9b2e5d5c2929dcfba1b538fbb079a839a9c) Co-authored-by: Miguel Grinberg Co-authored-by: Quentin Pradet --- docs/reference/dsl_how_to_guides.md | 131 +- docs/reference/dsl_tutorials.md | 2 +- elasticsearch/dsl/document_base.py | 51 +- elasticsearch/dsl/pydantic.py | 152 + examples/quotes/.gitignore | 24 + examples/quotes/README.md | 107 + examples/quotes/backend/pyproject.toml | 18 + examples/quotes/backend/quotes.csv | 843 ++++ examples/quotes/backend/quotes.py | 179 + examples/quotes/backend/requirements.txt | 163 + examples/quotes/eslint.config.js | 23 + examples/quotes/index.html | 13 + examples/quotes/package-lock.json | 3756 +++++++++++++++++ examples/quotes/package.json | 36 + examples/quotes/public/.keep | 0 examples/quotes/screenshot.png | Bin 0 -> 229765 bytes examples/quotes/src/App.tsx | 172 + examples/quotes/src/Quote.tsx | 104 + examples/quotes/src/index.css | 55 + examples/quotes/src/main.tsx | 24 + examples/quotes/src/models.tsx | 16 + examples/quotes/tsconfig.app.json | 28 + examples/quotes/tsconfig.json | 7 + examples/quotes/tsconfig.node.json | 26 + examples/quotes/vite.config.ts | 15 + noxfile.py | 1 + pyproject.toml | 2 + .../test_dsl/_async/test_document.py | 339 +- .../test_dsl/_sync/test_document.py | 339 +- utils/run-unasync-dsl.py | 1 + 30 files changed, 6402 insertions(+), 225 deletions(-) create mode 100644 elasticsearch/dsl/pydantic.py create mode 100644 examples/quotes/.gitignore create mode 100644 examples/quotes/README.md create mode 100644 examples/quotes/backend/pyproject.toml create mode 100644 examples/quotes/backend/quotes.csv create mode 100644 examples/quotes/backend/quotes.py create mode 100644 examples/quotes/backend/requirements.txt create mode 100644 examples/quotes/eslint.config.js create mode 100644 examples/quotes/index.html create mode 100644 examples/quotes/package-lock.json create mode 100644 examples/quotes/package.json create mode 100644 examples/quotes/public/.keep create mode 100644 examples/quotes/screenshot.png create mode 100644 examples/quotes/src/App.tsx create mode 100644 examples/quotes/src/Quote.tsx create mode 100644 examples/quotes/src/index.css create mode 100644 examples/quotes/src/main.tsx create mode 100644 examples/quotes/src/models.tsx create mode 100644 examples/quotes/tsconfig.app.json create mode 100644 examples/quotes/tsconfig.json create mode 100644 examples/quotes/tsconfig.node.json create mode 100644 examples/quotes/vite.config.ts diff --git a/docs/reference/dsl_how_to_guides.md b/docs/reference/dsl_how_to_guides.md index 66137c1ff..0d0370a23 100644 --- a/docs/reference/dsl_how_to_guides.md +++ b/docs/reference/dsl_how_to_guides.md @@ -630,7 +630,7 @@ For more comprehensive examples have a look at the [DSL examples](https://github ### Document [doc_type] -If you want to create a model-like wrapper around your documents, use the `Document` class. It can also be used to create all the necessary mappings and settings in elasticsearch (see `life-cycle` for details). +If you want to create a model-like wrapper around your documents, use the `Document` class (or the equivalent `AsyncDocument` for asynchronous applications). It can also be used to create all the necessary mappings and settings in Elasticsearch (see [Document life cycle](#life-cycle) below for details). ```python from datetime import datetime @@ -721,9 +721,19 @@ class Post(Document): published: bool # same as published = Boolean(required=True) ``` -It is important to note that when using `Field` subclasses such as `Text`, `Date` and `Boolean`, they must be given in the right-side of an assignment, as shown in examples above. Using these classes as type hints will result in errors. +::::{note} +When using `Field` subclasses such as `Text`, `Date` and `Boolean` to define attributes, these classes must be given in the right-hand side. + +```python +class Post(Document): + title = Text() # correct + subtitle: Text # incorrect +``` -Python types are mapped to their corresponding field types according to the following table: +Using a `Field` subclass as a Python type hint will result in errors. +:::: + +Python types are mapped to their corresponding `Field` types according to the following table: | Python type | DSL field | | --- | --- | @@ -735,7 +745,7 @@ Python types are mapped to their corresponding field types according to the foll | `datetime` | `Date(required=True)` | | `date` | `Date(format="yyyy-MM-dd", required=True)` | -To type a field as optional, the standard `Optional` modifier from the Python `typing` package can be used. When using Python 3.10 or newer, "pipe" syntax can also be used, by adding `| None` to a type. The `List` modifier can be added to a field to convert it to an array, similar to using the `multi=True` argument on the field object. +To type a field as optional, the standard `Optional` modifier from the Python `typing` package can be used. When using Python 3.10 or newer, "pipe" syntax can also be used, by adding `| None` to a type. The `List` modifier can be added to a field to convert it to an array, similar to using the `multi=True` argument on the `Field` object. ```python from typing import Optional, List @@ -763,7 +773,7 @@ class Post(Document): comments: List[Comment] # same as comments = Nested(Comment, required=True) ``` -Unfortunately it is impossible to have Python type hints that uniquely identify every possible Elasticsearch field type. To choose a field type that is different than the one that is assigned according to the table above, the desired field instance can be added explicitly as a right-side assignment in the field declaration. The next example creates a field that is typed as `Optional[str]`, but is mapped to `Keyword` instead of `Text`: +Unfortunately it is impossible to have Python type hints that uniquely identify every possible Elasticsearch `Field` type. To choose a type that is different than the one that is assigned according to the table above, the desired `Field` instance can be added explicitly as a right-side assignment in the field declaration. The next example creates a field that is typed as `Optional[str]`, but is mapped to `Keyword` instead of `Text`: ```python class MyDocument(Document): @@ -787,7 +797,7 @@ class MyDocument(Document): category: str = mapped_field(Keyword(), default="general") ``` -When using the `mapped_field()` wrapper function, an explicit field type instance can be passed as a first positional argument, as the `category` field does in the example above. +The `mapped_field()` wrapper function can optionally be given an explicit field type instance as a first positional argument, as the `category` field does in the example above to be defined as `Keyword` instead of the `Text` default. Static type checkers such as [mypy](https://mypy-lang.org/) and [pyright](https://github.com/microsoft/pyright) can use the type hints and the dataclass-specific options added to the `mapped_field()` function to improve type inference and provide better real-time code completion and suggestions in IDEs. @@ -829,7 +839,7 @@ s = MyDocument.search().sort(-MyDocument.created_at, MyDocument.title) When specifying sorting order, the `+` and `-` unary operators can be used on the class field attributes to indicate ascending and descending order. -Finally, the `ClassVar` annotation can be used to define a regular class attribute that should not be mapped to the Elasticsearch index: +Finally, it is also possible to define class attributes and request that they are ignored when building the Elasticsearch mapping. One way is to type attributes with the `ClassVar` annotation. Alternatively, the `mapped_field()` wrapper function accepts an `exclude` argument that can be set to `True`: ```python from typing import ClassVar @@ -837,9 +847,9 @@ from typing import ClassVar class MyDoc(Document): title: M[str] created_at: M[datetime] = mapped_field(default_factory=datetime.now) my_var: ClassVar[str] # regular class variable, ignored by Elasticsearch + anoter_custom_var: int = mapped_field(exclude=True) # also ignored by Elasticsearch ``` - #### Note on dates [_note_on_dates] The DSL module will always respect the timezone information (or lack thereof) on the `datetime` objects passed in or stored in Elasticsearch. Elasticsearch itself interprets all datetimes with no timezone information as `UTC`. If you wish to reflect this in your python code, you can specify `default_timezone` when instantiating a `Date` field: @@ -878,7 +888,7 @@ first.meta.id = 47 first.save() ``` -All the metadata fields (`id`, `routing`, `index` etc) can be accessed (and set) via a `meta` attribute or directly using the underscored variant: +All the metadata fields (`id`, `routing`, `index`, etc.) can be accessed (and set) via a `meta` attribute or directly using the underscored variant: ```python post = Post(meta={'id': 42}) @@ -961,12 +971,111 @@ first = Post.get(id=42) first.delete() ``` +#### Integration with Pydantic models + +::::{warning} +This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features. +:::: + +::::{note} +This feature is available in the Python Elasticsearch client starting with release 9.2.0. +:::: + +Applications that define their data models using [Pydantic](https://docs.pydantic.dev/latest/) can combine these +models with Elasticsearch DSL annotations. To take advantage of this option, Pydantic's `BaseModel` base class +needs to be replaced with `BaseESModel` (or `AsyncBaseESModel` for asynchronous applications), and then the model +can include type annotations for Pydantic and Elasticsearch both, as demonstrated in the following example: + +```python +from typing import Annotated +from pydantic import Field +from elasticsearch import dsl +from elasticsearch.dsl.pydantic import BaseESModel + +class Quote(BaseESModel): + quote: str + author: Annotated[str, dsl.Keyword()] + tags: Annotated[list[str], dsl.Keyword(normalizer="lowercase")] + embedding: Annotated[list[float], dsl.DenseVector()] = Field(init=False, default=[]) + + class Index: + name = "quotes" +``` + +In this example, the `quote` attribute is annotated with a `str` type hint. Both Pydantic and Elasticsearch use this +annotation. + +The `author` and `tags` attributes have a Python type hint and an Elasticsearch annotation, both wrapped with +Python's `typing.Annotated`. When using the `BaseESModel` class, the typing information intended for Elasticsearch needs +to be defined inside `Annotated`. + +The `embedding` attribute includes a base Python type and an Elasticsearch annotation in the same format as the +other fields, but it adds Pydantic's `Field` definition as a right-hand side assignment. + +Finally, any other items that need to be defined for the Elasticsearch document class, such as `class Index` and +`class Meta` entries (discussed later), can be added as well. + +The next example demonstrates how to define `Object` and `Nested` fields: + +```python +from typing import Annotated +from pydantic import BaseModel, Field +from elasticsearch import dsl +from elasticsearch.dsl.pydantic import BaseESModel + +class Phone(BaseModel): + type: Annotated[str, dsl.Keyword()] = Field(default="Home") + number: str + +class Person(BaseESModel): + name: str + main_phone: Phone # same as Object(Phone) + other_phones: list[Phone] # same as Nested(Phone) + + class Index: + name = "people" +``` + +Note that inner classes do not need to be defined with a custom base class; these should be standard Pydantic model +classes. The attributes defined in these classes can include Elasticsearch annotations, as long as they are given +in an `Annotated` type hint. + +All model classes that are created as described in this section function like normal Pydantic models and can be used +anywhere standard Pydantic models are used, but they have some added attributes: + +- `_doc`: a class attribute that is a dynamically generated `Document` class to use with the Elasticsearch index. +- `meta`: an attribute added to all models that includes Elasticsearch document metadata items such as `id`, `score`, etc. +- `to_doc()`: a method that converts the Pydantic model to an Elasticsearch document. +- `from_doc()`: a class method that accepts an Elasticsearch document as an argument and returns an equivalent Pydantic model. + +These are demonstrated in the examples below: + +```python +# create a Pydantic model +quote = Quote( + quote="An unexamined life is not worth living.", + author="Socrates", + tags=["phillosophy"] +) + +# save the model to the Elasticsearch index +quote.to_doc().save() + +# get a document from the Elasticsearch index as a Pydantic model +quote = Quote.from_doc(Quote._doc.get(id=42)) + +# run a search and print the Pydantic models +s = Quote._doc.search().query(Match(Quote._doc.quote, "life")) +for doc in s: + quote = Quote.from_doc(doc) + print(quote.meta.id, quote.meta.score, quote.quote) +``` #### Analysis [_analysis] To specify `analyzer` values for `Text` fields you can just use the name of the analyzer (as a string) and either rely on the analyzer being defined (like built-in analyzers) or define the analyzer yourself manually. -Alternatively you can create your own analyzer and have the persistence layer handle its creation, from our example earlier: +Alternatively, you can create your own analyzer and have the persistence layer handle its creation, from our example earlier: ```python from elasticsearch.dsl import analyzer, tokenizer @@ -1634,7 +1743,7 @@ for response in responses: ### Asynchronous Documents, Indexes, and more [_asynchronous_documents_indexes_and_more] -The `Document`, `Index`, `IndexTemplate`, `Mapping`, `UpdateByQuery` and `FacetedSearch` classes all have asynchronous versions that use the same name with an `Async` prefix. These classes expose the same interfaces as the synchronous versions, but any methods that perform I/O are defined as coroutines. +The `Document`, `BaseESModel`, `Index`, `IndexTemplate`, `Mapping`, `UpdateByQuery` and `FacetedSearch` classes all have asynchronous versions that use the same name with an `Async` prefix. These classes expose the same interfaces as the synchronous versions, but any methods that perform I/O are defined as coroutines. Auxiliary classes that do not perform I/O do not have asynchronous versions. The same classes can be used in synchronous and asynchronous applications. diff --git a/docs/reference/dsl_tutorials.md b/docs/reference/dsl_tutorials.md index 16224a13f..f53baefbe 100644 --- a/docs/reference/dsl_tutorials.md +++ b/docs/reference/dsl_tutorials.md @@ -134,7 +134,7 @@ In this example you can see: * retrieving and saving the object into Elasticsearch * accessing the underlying client for other APIs -You can see more in the `persistence` chapter. +You can see more in the [persistence](dsl_how_to_guides.md#_persistence_2) chapter. ## Pre-built Faceted Search [_pre_built_faceted_search] diff --git a/elasticsearch/dsl/document_base.py b/elasticsearch/dsl/document_base.py index 72f0364a4..d3f7eee09 100644 --- a/elasticsearch/dsl/document_base.py +++ b/elasticsearch/dsl/document_base.py @@ -34,6 +34,8 @@ overload, ) +from typing_extensions import _AnnotatedAlias + try: import annotationlib except ImportError: @@ -358,6 +360,10 @@ def __init__(self, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]): # the field has a type annotation, so next we try to figure out # what field type we can use type_ = annotations[name] + type_metadata = [] + if isinstance(type_, _AnnotatedAlias): + type_metadata = type_.__metadata__ + type_ = type_.__origin__ skip = False required = True multi = False @@ -404,6 +410,12 @@ def __init__(self, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]): # use best field type for the type hint provided field, field_kwargs = self.type_annotation_map[type_] # type: ignore[assignment] + # if this field does not have a right-hand value, we look in the metadata + # of the annotation to see if we find it there + for md in type_metadata: + if isinstance(md, (_FieldMetadataDict, Field)): + attrs[name] = md + if field: field_kwargs = { "multi": multi, @@ -416,17 +428,20 @@ def __init__(self, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]): # this field has a right-side value, which can be field # instance on its own or wrapped with mapped_field() attr_value = attrs[name] - if isinstance(attr_value, dict): + if isinstance(attr_value, _FieldMetadataDict): # the mapped_field() wrapper function was used so we need # to look for the field instance and also record any # dataclass-style defaults + if attr_value.get("exclude"): + # skip this field + continue attr_value = attrs[name].get("_field") default_value = attrs[name].get("default") or attrs[name].get( "default_factory" ) if default_value: field_defaults[name] = default_value - if attr_value: + if isinstance(attr_value, Field): value = attr_value if required is not None: value._required = required @@ -505,12 +520,19 @@ def __delete__(self, instance: Any) -> None: ... M = Mapped +class _FieldMetadataDict(dict[str, Any]): + """This class is used to identify metadata returned by the `mapped_field()` function.""" + + pass + + def mapped_field( field: Optional[Field] = None, *, init: bool = True, default: Any = None, default_factory: Optional[Callable[[], Any]] = None, + exclude: bool = False, **kwargs: Any, ) -> Any: """Construct a field using dataclass behaviors @@ -520,22 +542,25 @@ def mapped_field( options. :param field: The instance of ``Field`` to use for this field. If not provided, - an instance that is appropriate for the type given to the field is used. + an instance that is appropriate for the type given to the field is used. :param init: a value of ``True`` adds this field to the constructor, and a - value of ``False`` omits it from it. The default is ``True``. + value of ``False`` omits it from it. The default is ``True``. :param default: a default value to use for this field when one is not provided - explicitly. + explicitly. :param default_factory: a callable that returns a default value for the field, - when one isn't provided explicitly. Only one of ``factory`` and - ``default_factory`` can be used. + when one isn't provided explicitly. Only one of ``factory`` and + ``default_factory`` can be used. + :param exclude: Set to ``True`` to exclude this field from the Elasticsearch + index. """ - return { - "_field": field, - "init": init, - "default": default, - "default_factory": default_factory, + return _FieldMetadataDict( + _field=field, + init=init, + default=default, + default_factory=default_factory, + exclude=exclude, **kwargs, - } + ) @dataclass_transform(field_specifiers=(mapped_field,)) diff --git a/elasticsearch/dsl/pydantic.py b/elasticsearch/dsl/pydantic.py new file mode 100644 index 000000000..d1f8efaac --- /dev/null +++ b/elasticsearch/dsl/pydantic.py @@ -0,0 +1,152 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +from typing import Any, ClassVar, Dict, List, Optional, Tuple, Type + +from pydantic import BaseModel, Field, PrivateAttr +from typing_extensions import Annotated, Self, dataclass_transform + +from elasticsearch import dsl + + +class ESMeta(BaseModel): + """Metadata items associated with Elasticsearch documents.""" + + id: str = "" + index: str = "" + primary_term: int = 0 + seq_no: int = 0 + version: int = 0 + score: float = 0 + + +class _BaseModel(BaseModel): + meta: Annotated[ESMeta, dsl.mapped_field(exclude=True)] = Field( + default=ESMeta(), + init=False, + ) + + +class _BaseESModelMetaclass(type(BaseModel)): # type: ignore[misc] + """Generic metaclass methods for BaseEsModel and AsyncBaseESModel.""" + + @staticmethod + def process_annotations( + metacls: Type["_BaseESModelMetaclass"], annotations: Dict[str, Any] + ) -> Dict[str, Any]: + """Process Pydantic typing annotations and adapt them so that they can + be used to create the Elasticsearch document. + """ + updated_annotations = {} + for var, ann in annotations.items(): + if isinstance(ann, type(BaseModel)): + # an inner Pydantic model is transformed into an Object field + updated_annotations[var] = metacls.make_dsl_class( + metacls, dsl.InnerDoc, ann + ) + elif ( + hasattr(ann, "__origin__") + and ann.__origin__ in [list, List] + and isinstance(ann.__args__[0], type(BaseModel)) + ): + # an inner list of Pydantic models is transformed into a Nested field + updated_annotations[var] = List[ # type: ignore[assignment,misc] + metacls.make_dsl_class(metacls, dsl.InnerDoc, ann.__args__[0]) + ] + else: + updated_annotations[var] = ann + return updated_annotations + + @staticmethod + def make_dsl_class( + metacls: Type["_BaseESModelMetaclass"], + dsl_class: type, + pydantic_model: type, + pydantic_attrs: Optional[Dict[str, Any]] = None, + ) -> type: + """Create a DSL document class dynamically, using the structure of a + Pydantic model.""" + dsl_attrs = { + attr: value + for attr, value in dsl_class.__dict__.items() + if not attr.startswith("__") + } + pydantic_attrs = { + **(pydantic_attrs or {}), + "__annotations__": metacls.process_annotations( + metacls, pydantic_model.__annotations__ + ), + } + return type(dsl_class)( + f"_ES{pydantic_model.__name__}", + (dsl_class,), + { + **pydantic_attrs, + **dsl_attrs, + "__qualname__": f"_ES{pydantic_model.__name__}", + }, + ) + + +class BaseESModelMetaclass(_BaseESModelMetaclass): + """Metaclass for the BaseESModel class.""" + + def __new__(cls, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]) -> Any: + model = super().__new__(cls, name, bases, attrs) + model._doc = cls.make_dsl_class(cls, dsl.Document, model, attrs) + return model + + +class AsyncBaseESModelMetaclass(_BaseESModelMetaclass): + """Metaclass for the AsyncBaseESModel class.""" + + def __new__(cls, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]) -> Any: + model = super().__new__(cls, name, bases, attrs) + model._doc = cls.make_dsl_class(cls, dsl.AsyncDocument, model, attrs) + return model + + +@dataclass_transform(kw_only_default=True, field_specifiers=(Field, PrivateAttr)) +class BaseESModel(_BaseModel, metaclass=BaseESModelMetaclass): + _doc: ClassVar[Type[dsl.Document]] + + def to_doc(self) -> dsl.Document: + """Convert this model to an Elasticsearch document.""" + data = self.model_dump() + meta = {f"_{k}": v for k, v in data.pop("meta", {}).items() if v} + return self._doc(**meta, **data) + + @classmethod + def from_doc(cls, dsl_obj: dsl.Document) -> Self: + """Create a model from the given Elasticsearch document.""" + return cls(meta=ESMeta(**dsl_obj.meta.to_dict()), **dsl_obj.to_dict()) + + +@dataclass_transform(kw_only_default=True, field_specifiers=(Field, PrivateAttr)) +class AsyncBaseESModel(_BaseModel, metaclass=AsyncBaseESModelMetaclass): + _doc: ClassVar[Type[dsl.AsyncDocument]] + + def to_doc(self) -> dsl.AsyncDocument: + """Convert this model to an Elasticsearch document.""" + data = self.model_dump() + meta = {f"_{k}": v for k, v in data.pop("meta", {}).items() if v} + return self._doc(**meta, **data) + + @classmethod + def from_doc(cls, dsl_obj: dsl.AsyncDocument) -> Self: + """Create a model from the given Elasticsearch document.""" + return cls(meta=ESMeta(**dsl_obj.meta.to_dict()), **dsl_obj.to_dict()) diff --git a/examples/quotes/.gitignore b/examples/quotes/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/examples/quotes/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/quotes/README.md b/examples/quotes/README.md new file mode 100644 index 000000000..135a30b2b --- /dev/null +++ b/examples/quotes/README.md @@ -0,0 +1,107 @@ +# Quotes +Quotes database example, which demonstrates the Elasticsearch integration with +Pydantic models. This example features a React frontend and a FastAPI back end. + +![Quotes app screenshot](screenshot.png) + +## What is this? + +This directory contains a small application that demonstrates how easy it is +to set up a full-text and vector database using [Elasticsearch](https://www.elastic.co/elasticsearch), +while defining the data model with [Pydantic](https://docs.pydantic.dev/latest/). + +The application includes a FastAPI back end and a React front end. It ingests a +dataset of famous quotes into in an Elasticsearch index, and for each quote it +generates an embedding using the +[all-MiniLM-L6-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) +Sentence Transformers model. + +The dataset has about 850 famous quotes, each with their author and tags. The +data is a subset of a +[Kaggle dataset](https://www.kaggle.com/datasets/akmittal/quotes-dataset) that +appears to have been generated from quotes that were scraped from the Goodreads +[popular quotes](https://www.goodreads.com/quotes) page. + +## Requirements + +Please make sure you have the following installed: + +- Python 3.10 or newer +- Node.js 18 or newer + +## How To Run + +Follow these steps to install this demo on your computer: + +### Clone this repository + +Run the following command to install a copy of this project on your computer: + +```bash +git clone https://github.com/elastic/elasticsearch-py +cd examples/quotes +``` + +### Install the Node and Python dependencies + +Run the following command to set up the JavaScript and Python environment and +install all the dependencies: + +```bash +npm install +``` + +### Start a development Elasticsearch container + +You can use [start-local](https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart) +to start a small Elasticsearch instance. + +Use this command to launch the instance (Docker and Docker Compose are required): + +```bash +curl -fsSL https://elastic.co/start-local | sh +``` + +Once your Elasticsearch instead is deployed, create an environment variable called +`ELASTICSEARCH_URL`, making sure it includes the password generated by start-local. +Example: + +```bash +export ELASTICSEARCH_URL=http://elastic:your-password-here@localhost:9200 +``` + +### Create the quotes database + +Run this command in your terminal: + +```bash +npm run ingest +``` + +Note that the `ELASTICSEARCH_URL` variable must be defined in the terminal +session in which you run this command. + +This task should take a minute or less. The GPU, if available, is used optimize +the generation of the embeddings. + +### Start the back end + +Run this command in your terminal: + +```bash +npm run backend +``` + +Note that the `ELASTICSEARCH_URL` variable must be defined in the terminal +session in which you run this command. + +### Start the front end + +Open a second terminal window and run this command: + +```bash +npm run dev +``` + +You can now navigate to `http://localhost:5173` on your web browser to access +the application. diff --git a/examples/quotes/backend/pyproject.toml b/examples/quotes/backend/pyproject.toml new file mode 100644 index 000000000..a91e2c53c --- /dev/null +++ b/examples/quotes/backend/pyproject.toml @@ -0,0 +1,18 @@ +[project] +name = "quotes" +version = "0.1" +dependencies = [ + "elasticsearch[async]>=9,<10", + "fastapi", + "orjson", + "sentence-transformers", + "uvicorn", +] + +[project.optional-dependencies] +dev = [ +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/examples/quotes/backend/quotes.csv b/examples/quotes/backend/quotes.csv new file mode 100644 index 000000000..44d95eb7a --- /dev/null +++ b/examples/quotes/backend/quotes.csv @@ -0,0 +1,843 @@ +"quote","author","tags" +"Two things are infinite: the universe and human stupidity; and I'm not sure about the universe.","Albert Einstein","attributed-no-source,human-nature,humor,infinity,philosophy,science,stupidity,universe" +"In three words I can sum up everything I've learned about life: it goes on.","Robert Frost","moving-on,learned" +"If you tell the truth, you don't have to remember anything.","Mark Twain","lies,lying,memory,truth" +"I've learned that people will forget what you said, people will forget what you did, but people will never forget how you made them feel.","Maya Angelou","61419-likes" +"There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.","Albert Einstein","attributed-no-source,inspirational,life,live,miracle,miracles" +"Good friends, good books, and a sleepy conscience: this is the ideal life.","Mark Twain","books,contentment,friends,friendship,life" +"Whenever you find yourself on the side of the majority, it is time to reform (or pause and reflect).","Mark Twain","individuality,majority,minority,pause,reflect,wisdom" +"The man who does not read has no advantage over the man who cannot read.","Mark Twain","aliteracy,attributed-no-source,literacy" +"Outside of a dog, a book is man's best friend. Inside of a dog it's too dark to read.","Groucho Marx","animals,books,dogs,friends,humor" +"I am enough of an artist to draw freely upon my imagination. Imagination is more important than knowledge. Knowledge is limited. Imagination encircles the world.","Albert Einstein","1929,imagination,inspirational,viereck-interview" +"Never put off till tomorrow what may be done day after tomorrow just as well.","Mark Twain","humor,procrastination" +"If you can't explain it to a six year old, you don't understand it yourself.","Albert Einstein","attributed-no-source,simplicity,understand" +"Everything you can imagine is real.","Pablo Picasso","art,imagination,inspirational,life" +"If you want your children to be intelligent, read them fairy tales. If you want them to be more intelligent, read them more fairy tales.","Albert Einstein","attributed-no-source,children,fairy-tales" +"Logic will get you from A to Z; imagination will get you everywhere.","Albert Einstein","attributed-no-source,imagination" +"I find television very educating. Every time somebody turns on the set, I go into the other room and read a book.","Groucho Marx","books,humor,reading,television" +"When one door of happiness closes, another opens; but often we look so long at the closed door that we do not see the one which has been opened for us.","Helen Keller","inspirational" +"Life is like riding a bicycle. To keep your balance, you must keep moving.","Albert Einstein","balance,moving,bicycle" +"If everyone is moving forward together, then success takes care of itself.","Henry Ford","together,care,moving" +"Love is like a beautiful flower which I may not touch, but whose fragrance makes the garden a place of delight just the same.","Helen Keller","flower,beautiful,garden" +"What's money? A man is a success if he gets up in the morning and goes to bed at night and in between does what he wants to do.","Bob Dylan","morning,money,night" +"The most beautiful thing we can experience is the mysterious. It is the source of all true art and science.","Albert Einstein","experience,science" +"You may say I'm a dreamer, but I'm not the only one. I hope someday you'll join us. And the world will live as one.","John Lennon","beatles,connection,dreamers,dreaming,dreams,hope,inspirational,peace" +"Optimism is the faith that leads to achievement. Nothing can be done without hope and confidence.","Helen Keller","motivational,faith" +"Love is an irresistible desire to be irresistibly desired.","Robert Frost","love" +"Love is the flower you've got to let grow.","John Lennon","flower,grow" +"Success depends upon previous preparation, and without such preparation there is sure to be failure.","Confucius","failure,sure,depends" +"Imagine all the people living life in peace. You may say I'm a dreamer, but I'm not the only one. I hope someday you'll join us, and the world will be as one.","John Lennon","peace,hope,dreamer" +"The question isn't who is going to let me; it's who is going to stop me.","Ayn Rand","15880-likes" +"Coming together is a beginning; keeping together is progress; working together is success.","Henry Ford","together,progress" +"I have never let my schooling interfere with my education.","Mark Twain","attributed-no-source,education" +"Try not to become a man of success, but rather try to become a man of value.","Albert Einstein","value,rather" +"Anyone who has never made a mistake has never tried anything new.","Albert Einstein","attributed-no-source,mistakes" +"Action is the foundational key to all success.","Pablo Picasso","action,key" +"The fear of death follows from the fear of life. A man who lives fully is prepared to die at any time.","Mark Twain","death,life" +"′Classic′ - a book which people praise and don't read.","Mark Twain","books,classic,reading" +"The biggest adventure you can take is to live the life of your dreams.","Oprah Winfrey","dreams,adventure,biggest" +"Alcohol may be man's worst enemy, but the bible says love your enemy.","Frank Sinatra","alcohol,bible,drinking,enemies,religion" +"Happiness lies in the joy of achievement and the thrill of creative effort.","Franklin D. Roosevelt","happiness" +"All you need in this life is ignorance and confidence, and then success is sure.","Mark Twain","success,ignorance" +"Love recognizes no barriers. It jumps hurdles, leaps fences, penetrates walls to arrive at its destination full of hope.","Maya Angelou","love" +"The foundation stones for a balanced success are honesty, character, integrity, faith, love and loyalty.","Zig Ziglar","success,faith,character" +"Strive not to be a success, but rather to be of value.","Albert Einstein","business,success,value" +"I've failed over and over and over again in my life and that is why I succeed.","Michael Jordan","success,succeed,again" +"True art is characterized by an irresistible urge in the creative artist.","Albert Einstein","creative,artist,true" +"Everyone has inside of him a piece of good news. The good news is that you don't know how great you can be! How much you can love! What you can accomplish! And what your potential is!","Anne Frank","love,good,great" +"I believe that being successful means having a balance of success stories across the many areas of your life. You can't truly be considered successful in your business life if your home life is in shambles.","Zig Ziglar","home,life,business" +"I am enough of an artist to draw freely upon my imagination.","Albert Einstein","artist,draw,freely" +"It is easy to hate and it is difficult to love. This is how the whole scheme of things works. All good things are difficult to achieve; and bad things are very easy to get.","Confucius","good,achieve,hate" +"Choose a job you love, and you will never have to work a day in your life.","Confucius","work,love,choose" +"If you have only one smile in you give it to the people you love.","Maya Angelou","valentine's-day" +"Sculpture is the art of the intelligence.","Pablo Picasso","sculpture" +"A lie can travel half way around the world while the truth is putting on its shoes.","Mark Twain","misattributed-mark-twain,truth" +"Only a life lived for others is a life worthwhile.","Albert Einstein","others,worthwhile,lived" +"Never tell the truth to people who are not worthy of it.","Mark Twain","truth" +"Success is like reaching an important birthday and finding you're exactly the same.","Audrey Hepburn","birthday,finding,reaching" +"My great hope is to laugh as much as I cry; to get my work done and try to love somebody and have the courage to accept the love in return.","Maya Angelou","being-loved,courage,laugh,love,loving" +"The ladder of success is best climbed by stepping on the rungs of opportunity.","Ayn Rand","best,ladder,stepping" +"Learn from yesterday, live for today, hope for tomorrow. The important thing is not to stop questioning.","Albert Einstein","today,learn,tomorrow" +"I believe that unarmed truth and unconditional love will have the final word in reality. This is why right, temporarily defeated, is stronger than evil triumphant.","Martin Luther King Jr.","good-and-evil,love,truth" +"We must accept finite disappointment, but never lose infinite hope.","Martin Luther King Jr.","hope" +"We have always held to the hope, the belief, the conviction that there is a better life, a better world, beyond the horizon.","Franklin D. Roosevelt","hope,conviction,belief" +"I simply can't build my hopes on a foundation of confusion, misery and death... I think... peace and tranquillity will return again.","Anne Frank","death,peace,foundation" +"You can make positive deposits in your own economy every day by reading and listening to powerful, positive, life-changing content and by associating with encouraging and hope-building people.","Zig Ziglar","positive,reading,listening" +"Youth is easily deceived because it is quick to hope.","Aristotle","youth,deceived,quick" +"Speak the truth, do not yield to anger; give, if thou art asked for little; by these three steps thou wilt go near the gods.","Confucius","truth,anger,speak" +"Knowing yourself is the beginning of all wisdom.","Aristotle","introspection,self-discovery,wisdom" +"Art washes away from the soul the dust of everyday life.","Pablo Picasso","art" +"A person with a new idea is a crank until the idea succeeds.","Mark Twain","crank,succeeds,until" +"I would rather walk with a friend in the dark, than alone in the light.","Helen Keller","friendship" +"Remembering that I'll be dead soon is the most important tool I've ever encountered to help me make the big choices in life. Because almost everything - all external expectations, all pride, all fear of embarrassment or failure - these things just fall away in the face of death, leaving only what is truly important.","Steve Jobs","failure,death,fear" +"The overwhelming majority of Americans are possessed of two great qualities a sense of humor and a sense of proportion.","Franklin D. Roosevelt","great,qualities,majority" +"Part of the secret of a success in life is to eat what you like and let the food fight it out inside.","Mark Twain","food,life,fight" +"Love is a better teacher than duty.","Albert Einstein","teacher,duty" +"In the flush of love's light, we dare be brave. And suddenly we see that love costs all we are, and will ever be. Yet it is only love which sets us free.","Maya Angelou","light,free,brave" +"I speak to everyone in the same way, whether he is the garbage man or the president of the university.","Albert Einstein","life,respect" +"You cannot climb the ladder of success dressed in the costume of failure.","Zig Ziglar","failure,ladder,climb" +"Keep away from people who try to belittle your ambitions. Small people always do that, but the really great make you feel that you, too, can become great.","Mark Twain","greatness" +"Your work is going to fill a large part of your life, and the only way to be truly satisfied is to do what you believe is great work. And the only way to do great work is to love what you do. If you haven't found it yet, keep looking. Don't settle. As with all matters of the heart, you'll know when you find it.","Steve Jobs","life,love,work" +"The beauty of a woman must be seen from in her eyes, because that is the doorway to her heart, the place where love resides.","Audrey Hepburn","beauty,eyes,heart" +"A person will sometimes devote all his life to the development of one part of his body - the wishbone.","Robert Frost","body,devote,wishbone" +"People are basically the same the world over. Everybody wants the same things - to be happy, to be healthy, to be at least reasonably prosperous, and to be secure. They want friends, peace of mind, good family relationships, and hope that tomorrow is going to be even better than today.","Zig Ziglar","family,good,peace" +"How wonderful it is that nobody need wait a single moment before starting to improve the world.","Anne Frank","moment,improve,wait" +"Nothing is impossible, the word itself says 'I'm possible'!","Audrey Hepburn","impossible,possible" +"We should measure welfare's success by how many people leave welfare, not by how many are added.","Ronald Reagan","welfare,measure,leave" +"We've got this gift of love, but love is like a precious plant. You can't just accept it and leave it in the cupboard or just think it's going to get on by itself. You've got to keep watering it. You've got to really look after it and nurture it.","John Lennon","relationship,nurture,gift" +"I write some country music. There's a song called 'I Hope You Dance.' Incredible. I was going to write that poem; somebody beat me to it.","Maya Angelou","music,dance,song" +"Painting is just another way of keeping a diary.","Pablo Picasso","painting,diary,keeping" +"If you find it in your heart to care for somebody else, you will have succeeded.","Maya Angelou","care,heart,succeeded" +"There is a very fine line between loving life and being greedy for it.","Maya Angelou","greed,life" +"We love the things we love for what they are.","Robert Frost","love,poetry" +"I decided, very early on, just to accept life unconditionally; I never expected it to do anything special for me, yet I seemed to accomplish far more than I had ever hoped. Most of the time it just happened to me without my ever seeking it.","Audrey Hepburn","time,accept,special" +"Count your age by friends, not years. Count your life by smiles, not tears.","John Lennon","happiness,timelessness,wisdom" +"I remember a specific moment, watching my grandmother hang the clothes on the line, and her saying to me, 'you are going to have to learn to do this,' and me being in that space of awareness and knowing that my life would not be the same as my grandmother's life.","Oprah Winfrey","space,learn,moment" +"When you're in jail, a good friend will be trying to bail you out. A best friend will be in the cell next to you saying, 'Damn, that was fun'.","Groucho Marx","friends,humor,jail" +"I believe that a simple and unassuming manner of life is best for everyone, best both for the body and the mind.","Albert Einstein","best,simple,body" +"When you are courting a nice girl an hour seems like a second. When you sit on a red-hot cinder a second seems like an hour. That's relativity.","Albert Einstein","humor" +"In a good bookroom you feel in some mysterious way that you are absorbing the wisdom contained in all the books through your skin, without even opening them.","Mark Twain","bookroom,books,libraries,wisdom" +"Loving someone liberates the lover as well as the beloved. And that kind of love comes with age.","Maya Angelou","age,lover,loving" +"For the past 33 years, I have looked in the mirror every morning and asked myself: 'If today were the last day of my life, would I want to do what I am about to do today?' And whenever the answer has been 'No' for too many days in a row, I know I need to change something.","Steve Jobs","change,morning,mirror" +"Never memorize something that you can look up.","Albert Einstein","humor,science" +"He that lives upon hope will die fasting.","Benjamin Franklin","die,fasting,lives" +"I always entertain great hopes.","Robert Frost","great,hopes,entertain" +"Lord save us all from old age and broken health and a hope tree that has lost the faculty of putting out blossoms.","Mark Twain","health,age,tree" +"Hope is a waking dream.","Aristotle","dreams,hope" +"In the end, that's what this election is about. Do we participate in a politics of cynicism or a politics of hope?","Barack Obama","politics,election,cynicism" +"A clever person solves a problem. A wise person avoids it.","Albert Einstein","8292-likes" +"Reader, suppose you were an idiot. And suppose you were a member of Congress. But I repeat myself.","Mark Twain","humor,politics" +"Substitute 'damn' every time you're inclined to write 'very;' your editor will delete it and the writing will be just as it should be.","Mark Twain","humor,writing" +"Science without religion is lame, religion without science is blind.","Albert Einstein","religion,science" +"I love people who make me laugh. I honestly think it's the thing I like most, to laugh. It cures a multitude of ills. It's probably the most important thing in a person.","Audrey Hepburn","love,honestly,laugh" +"When someone shows you who they are believe them; the first time.","Maya Angelou","oprah-s-thank-you-game,people" +"Reality is merely an illusion, albeit a very persistent one.","Albert Einstein","reality" +"I attribute my success to this - I never gave or took any excuse.","Florence Nightingale","motivational,excuse" +"Whatever you want to do, if you want to be great at it, you have to love it and be able to make sacrifices for it.","Maya Angelou","great,whatever,sacrifices" +"Don’t go around saying the world owes you a living. The world owes you nothing. It was here first.","Mark Twain","living,world" +"I believe in everything until it's disproved. So I believe in fairies, the myths, dragons. It all exists, even if it's in your mind. Who's to say that dreams and nightmares aren't as real as the here and now?","John Lennon","beatles,dragons,dreamers,dreaming,dreams,fairies,faith,mythology,nightmares,reality,truth" +"Knowledge is love and light and vision.","Helen Keller","knowledge,light,vision" +"Honesty and integrity are absolutely essential for success in life - all areas of life. The really good news is that anyone can develop both honesty and integrity.","Zig Ziglar","success,good,integrity" +"Life is what happens while you are busy making other plans.","John Lennon","busy,plans,happens" +"The more one does and sees and feels, the more one is able to do, and the more genuine may be one's appreciation of fundamental things like home, and love, and understanding companionship.","Amelia Earhart","home,genuine,feels" +"If we knew what it was we were doing, it would not be called research, would it?","Albert Einstein","science" +"Don't go around saying the world owes you a living. The world owes you nothing. It was here first.","Mark Twain","owes,living,saying" +"Faith is taking the first step even when you can't see the whole staircase.","Martin Luther King Jr.","inspirational-faith" +"The superior man makes the difficulty to be overcome his first interest; success only comes later.","Confucius","overcome,difficulty" +"I have no special talents. I am only passionately curious.","Albert Einstein","inspirational-life" +"Good friends, good books and a sleepy conscience: this is the ideal life.","Mark Twain","good,books,conscience" +"Well, Art is Art, isn't it? Still, on the other hand, water is water. And east is east and west is west and if you take cranberries and stew them like applesauce they taste much more like prunes than rhubarb does. Now you tell me what you know.","Groucho Marx","water,hand,taste" +"When I was 5 years old, my mother always told me that happiness was the key to life. When I went to school, they asked me what I wanted to be when I grew up. I wrote down ‘happy’. They told me I didn’t understand the assignment, and I told them they didn’t understand life.","John Lennon","inspirational,life" +"Whether you think you can, or you think you can't--you're right.","Henry Ford","thinking" +"In the end, we will remember not the words of our enemies, but the silence of our friends.","Martin Luther King Jr.","inspirational" +"I should have no objection to go over the same life from its beginning to the end: requesting only the advantage authors have, of correcting in a second edition the faults of the first.","Benjamin Franklin","beginning,advantage,second" +"Think of all the beauty still left around you and be happy.","Anne Frank","anne,beauty,frank,happy" +"The most important thing is to enjoy your life—to be happy—it's all that matters.","Audrey Hepburn","happiness,life" +"If a cluttered desk is a sign of a cluttered mind, of what, then, is an empty desk a sign?","Albert Einstein","einstein,human,humor,philosophy,stupidity" +"Never be bullied into silence. Never allow yourself to be made a victim. Accept no one’s definition of your life; define yourself.","Robert Frost","6617-likes" +"Everything is clearer when you're in love.","John Lennon","love" +"I did not attend his funeral, but I sent a nice letter saying I approved of it.","Mark Twain","classic-insult,funeral,funny,humor" +"What is a friend? A single soul dwelling in two bodies.","Aristotle","friendship,soul" +"God created war so that Americans would learn geography.","Mark Twain","americans,geography,war" +"My mission in life is not merely to survive, but to thrive; and to do so with some passion, some compassion, some humor, and some style.","Maya Angelou","life,humor,compassion" +"Love is the greatest refreshment in life.","Pablo Picasso","life,greatest" +"We delight in the beauty of the butterfly, but rarely admit the changes it has gone through to achieve that beauty.","Maya Angelou","inspiration" +"Gravitation is not responsible for people falling in love.","Albert Einstein","love" +"As usual, there is a great woman behind every idiot.","John Lennon","beatles,men,women" +"Either write something worth reading or do something worth writing.","Benjamin Franklin","hmmm" +"If you can't fly then run, if you can't run then walk, if you can't walk then crawl, but whatever you do you have to keep moving forward.","Martin Luther King Jr.","inspirational" +"If people are good only because they fear punishment, and hope for reward, then we are a sorry lot indeed.","Albert Einstein","good,fear,punishment" +"I was gratified to be able to answer promptly, and I did. I said I didn’t know.","Mark Twain","humor,knowledge" +"But who prays for Satan? Who, in eighteen centuries, has had the common humanity to pray for the one sinner that needed it most?","Mark Twain","humor-satan" +"If money is your hope for independence you will never have it. The only real security that a man will have in this world is a reserve of knowledge, experience, and ability.","Henry Ford","knowledge,money,experience" +"I love to see a young girl go out and grab the world by the lapels. Life's a bitch. You've got to go out and kick ass.","Maya Angelou","humor,inspirational,life" +"Never allow someone to be your priority while allowing yourself to be their option.","Mark Twain","heartbreak" +"The important thing is to not stop questioning. Curiosity has its own reason for existence. One cannot help but be in awe when he contemplates the mysteries of eternity, of life, of the marvelous structure of reality. It is enough if one tries merely to comprehend a little of this mystery each day.—"Old Man's Advice to Youth: 'Never Lose a Holy Curiosity.'"LIFE Magazine (2 May 1955) p. 64","Albert Einstein","1955,curiosity,mystery,physics,science" +"A youth, when at home, should be filial and, abroad, respectful to his elders. He should be earnest and truthful. He should overflow in love to all and cultivate the friendship of the good. When he has time and opportunity, after the performance of these things, he should employ them in polite studies.","Confucius","love,time,home" +"No tears in the writer, no tears in the reader. No surprise in the writer, no surprise in the reader.","Robert Frost","reading,writing" +"Try not to become a man of success. Rather become a man of value.","Albert Einstein","adulthood,success,value" +"Turn your wounds into wisdom.","Oprah Winfrey","experience,inspirational,pain,wisdom,wounds" +"I thank God I'm myself and for the life I'm given to live and for friends and lovers and beloveds, and I thank God for knowing that all those people have already paid for me.","Maya Angelou","god,knowing,lovers" +"Every child is an artist. The problem is how to remain an artist once he grows up.","Pablo Picasso","art,attributed-no-source" +"Loyalty to country ALWAYS. Loyalty to government, when it deserves it.","Mark Twain","patriotism,politics" +"Wrinkles should merely indicate where the smiles have been.","Mark Twain","age" +"Courage is the most important of all the virtues because without courage, you can't practice any other virtue consistently.","Maya Angelou","character,consistency,courage,determination,essence,ethos,fortitude,goodness,inspiration,life-lessons,persistence,resolve,self-reliance,strength,virtue,virtues" +"Any fool can know. The point is to understand.","Albert Einstein","knowledge,learning,understanding,wisdom" +"Life would be infinitely happier if we could only be born at the age of eighty and gradually approach eighteen.","Mark Twain","age,born,approach" +"And I loved Fats Waller. I love his instrumental abilities, his vocal abilities and his sense of humor.","Paul McCartney","humor,loved,ability" +"The best and most beautiful things in the world cannot be seen or even touched. They must be felt with the heart","Helen Keller","by-anne-sullivan,emotions,feelings" +"No one wants to die. Even people who want to go to heaven don't want to die to get there. And yet death is the destination we all share. No one has ever escaped it. And that is as it should be, because Death is very likely the single best invention of Life. It is Life's change agent. It clears out the old to make way for the new.","Steve Jobs","death,change,best" +"Rhetoric may be defined as the faculty of observing in any given case the available means of persuasion. This is not a function of any other art.","Aristotle","persuasion,rhetoric" +"I don't trust people who don't love themselves and tell me, 'I love you.' ... There is an African saying which is: Be careful when a naked person offers you a shirt.","Maya Angelou","1997,annie-clark-tanner-lecture,inspirational,love,trust" +"I've missed more than 9000 shots in my career. I've lost almost 300 games. 26 times, I've been trusted to take the game winning shot and missed. I've failed over and over and over again in my life. And that is why I succeed.","Michael Jordan","inspirational" +"Once you can accept the universe as matter expanding into nothing that is something, wearing stripes with plaid comes easy.","Albert Einstein","funny" +"Microsoft has had two goals in the last 10 years. One was to copy the Mac, and the other was to copy Lotus' success in the spreadsheet - basically, the applications business. And over the course of the last 10 years, Microsoft accomplished both of those goals. And now they are completely lost.","Steve Jobs","business,goals,lost" +"Try to look at your weakness and convert it into your strength. That's success.","Zig Ziglar","strength,weakness,convert" +"A woman's heart should be so hidden in God that a man has to seek Him just to find her.","Maya Angelou","inspirational" +"What material success does is provide you with the ability to concentrate on other things that really matter. And that is being able to make a difference, not only in your own life, but in other people's lives.","Oprah Winfrey","life,ability,difference" +"You can't connect the dots looking forward; you can only connect them looking backwards. So you have to trust that the dots will somehow connect in your future. You have to trust in something - your gut, destiny, life, karma, whatever. This approach has never let me down, and it has made all the difference in my life.","Steve Jobs","future,trust,karma" +"What we have once enjoyed we can never lose. All that we love deeply becomes a part of us.","Helen Keller","lose,becomes,enjoyed" +"Music was my refuge. I could crawl into the space between the notes and curl my back to loneliness.","Maya Angelou","loneliness,music,refuge" +"Love many things, for therein lies the true strength, and whosoever loves much performs much, and can accomplish much, and what is done in love is done well.","Vincent Van Gogh","strength,true,accomplish" +"By three methods we may learn wisdom: First, by reflection, which is noblest; Second, by imitation, which is easiest; and third by experience, which is the bitterest.","Confucius","wisdom" +"Humor must not professedly teach and it must not professedly preach, but it must do both if it would live forever.","Mark Twain","teach,forever,preach" +"I met Woz when I was 13, at a friend's garage. He was about 18. He was, like, the first person I met who knew more electronics than I did at that point. We became good friends, because we shared an interest in computers and we had a sense of humor. We pulled all kinds of pranks together.","Steve Jobs","good,computers,together" +"The purpose of art is washing the dust of daily life off our souls.","Pablo Picasso","life,daily,purpose" +"If I were not a physicist, I would probably be a musician. I often think in music. I live my daydreams in music. I see my life in terms of music.","Albert Einstein","music" +"Only in the darkness can you see the stars.","Martin Luther King Jr.","hope,inspirational" +"No person is your friend who demands your silence, or denies your right to grow.","Alice Walker","silence,friend,grow" +"The Bush Administration's failure to be consistently involved in helping Israel achieve peace with the Palestinians has been both wrong for our friendship with Israel, as well as badly damaging to our standing in the Arab world.","Barack Obama","failure,peace,achieve" +"The most difficult thing is the decision to act, the rest is merely tenacity. The fears are paper tigers. You can do anything you decide to do. You can act to change and control your life; and the procedure, the process is its own reward.","Amelia Earhart","change,decision,control" +"The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.","Albert Einstein","change,deep-thoughts,thinking,world" +"Be true to the game, because the game will be true to you. If you try to shortcut the game, then the game will shortcut you. If you put forth the effort, good things will be bestowed upon you. That's truly about the game, and in some ways that's about life too.","Michael Jordan","good,effort,game" +"Art is the lie that enables us to realize the truth.","Pablo Picasso","art,truth" +"The more I see, the less I know for sure.","John Lennon","beatles,life,perception,truth" +"I do not fear death. I had been dead for billions and billions of years before I was born, and had not suffered the slightest inconvenience from it.","Mark Twain","death,inspirational" +"Books are for people who wish they were somewhere else.","Mark Twain","books-reading" +"Those who educate children well are more to be honored than they who produce them; for these only gave them life, those the art of living well.","Aristotle","greek,mentors,philosophy,teachers" +"What would men be without women? Scarce, sir...mighty scarce.","Mark Twain","humor,men,women" +"The secret source of humor is not joy but sorrow; there is no humor in Heaven.","Mark Twain","joy,heaven,sorrow" +"I know not with what weapons World War III will be fought, but World War IV will be fought with sticks and stones.","Albert Einstein","albert-einstein,future,war,wisdom" +"Success is liking yourself, liking what you do, and liking how you do it.","Maya Angelou","4051-likes" +"You never fail until you stop trying.","Albert Einstein","inspirational" +"We all know that Art is not truth. Art is a lie that makes us realize the truth, at least the truth that is given to us to understand.","Pablo Picasso","truth,lie,understand" +"Great spirits have always encountered violent opposition from mediocre minds.","Albert Einstein","greatness,opposition,spirit" +"I did then what I knew how to do. Now that I know better, I do better.","Maya Angelou","attributed,attributed-no-source,education,intelligence,knowledge,unsourced" +"Some say the world will end in fire,Some say in ice.From what I've tasted of desire,I hold with those who favor fire. But if it had to perish twiceI think I know enough of hateTo say that for destruction iceIs also greatAnd would suffice.","Robert Frost","poetry" +"Sanity and happiness are an impossible combination.","Mark Twain","happiness,sanity" +"My mission in life is not merely to survive, but to thrive; and to do so with some passion, some compassion, some humor, and some style","Maya Angelou","3758-likes" +"A poem begins as a lump in the throat, a sense of wrong, a homesickness, a lovesickness.","Robert Frost","poetry" +"Clothes make the man. Naked people have little or no influence on society.","Mark Twain","clothing" +"Don't part with your illusions. When they are gone you may still exist, but you have ceased to live.","Mark Twain","illusions,life" +"I can shake off everything as I write; my sorrows disappear, my courage is reborn.","Anne Frank","writing" +"The aim of art is to represent not the outward appearance of things, but their inward significance.","Aristotle","art,philosophy" +"Our brand of democracy is hard. But I can promise that a year from now, when I no longer hold this office, I'll be right there with you as a citizen - inspired by those voices of fairness and vision, of grit and good humor and kindness that have helped America travel so far.","Barack Obama","travel,good,kindness" +"If you hear a voice within you say 'you cannot paint,' then by all means paint, and that voice will be silenced.","Vincent Van Gogh","voice,within,paint" +"From the moment I picked up your book until I put it down, I was convulsed with laughter. Some day I intend reading it.","Groucho Marx","books,humor,reading" +"The most common way people give up their power is by thinking they don't have any.","Alice Walker","inspirational" +"I'll always stay connected with Apple. I hope that throughout my life I'll sort of have the thread of my life and the thread of Apple weave in and out of each other, like a tapestry. There may be a few years when I'm not there, but I'll always come back.","Steve Jobs","life,connected,apple" +"There can be no deep disappointment where there is not deep love.","Martin Luther King Jr.","justice,love,segregation" +"Have enough courage to trust love one more time and always one more time.","Maya Angelou","courage,love,trust" +"Achievement of your happiness is the only moral purpose of your life, and that happiness, not pain or mindless self-indulgence, is the proof of your moral integrity, since it is the proof and the result of your loyalty to the achievement of your values.","Ayn Rand","happiness,pain,integrity" +"If you pick up a starving dog and make him prosperous he will not bite you. This is the principal difference between a dog and man.","Mark Twain","animals,dogs" +"When I was a boy of 14, my father was so ignorant I could hardly stand to have the old man around. But when I got to be 21, I was astonished at how much the old man had learned in seven years.","Mark Twain","arrogance,humor,ignorance" +"Everything human is pathetic. The secret source of humor itself is not joy but sorrow. There is no humor in heaven.","Mark Twain","joy,heaven,sorrow" +"Always do what is right. It will gratify half of mankind and astound the other.","Mark Twain","morals" +"The artist is a receptacle for emotions that come from all over the place: from the sky, from the earth, from a scrap of paper, from a passing shape, from a spider's web.","Pablo Picasso","sky,earth,emotions" +"There is more to sex appeal than just measurements. I don't need a bedroom to prove my womanliness. I can convey just as much sex appeal, picking apples off a tree or standing in the rain.","Audrey Hepburn","sex-appeal,women" +"Education is the ability to listen to almost anything without losing your temper or your self-confidence.","Robert Frost","education" +"Perfect friendship is the friendship of men who are good, and alike in excellence; for these wish well alike to each other qua good, and they are good in themselves.","Aristotle","good,men,excellence" +"It is not that I'm so smart. But I stay with the questions much longer.","Albert Einstein","intelligence,learning,wisdom" +"A woman without a man is like a fish without a bicycle.","Gloria Steinem","feminism,humour" +"The trouble is not in dying for a friend, but in finding a friend worth dying for.","Mark Twain","3011-likes" +"The more you praise and celebrate your life, the more there is in life to celebrate.","Oprah Winfrey","happiness,life" +"One thing you can't hide - is when you're crippled inside.","John Lennon","beatles,emotions,hypocrisy,pain,suffering" +"Fighting for one's freedom, struggling towards being free, is like struggling to be a poet or a good Christian or a good Jew or a good Muslim or good Zen Buddhist. You work all day long and achieve some kind of level of success by nightfall, go to sleep and wake up the next morning with the job still to be done. So you start all over again.","Maya Angelou","work,morning,freedom" +"If you don't read the newspaper, you're uninformed. If you read the newspaper, you're mis-informed.","Mark Twain","newspapers" +"There are millions of Americans outside Washington who are tired of stale political arguments and are moving this country forward. They believe, and I believe, that here in America, our success should depend not on accident of birth, but the strength of our work ethic and the scope of our dreams.","Barack Obama","work,strength,dreams" +"Creativity is intelligence having fun.","Albert Einstein","2946-likes" +"Wheresoever you go, go with all your heart.","Confucius","2940-likes" +"The measure of intelligence is the ability to change.","Albert Einstein","adaptation,flexibility,intelligence,open-mindedness,wisdom" +"Everything has beauty, but not everyone sees it.","Confucius","beauty,everything,seeing" +"Learn to value yourself, which means: fight for your happiness.","Ayn Rand","fight,happiness,learn" +"The world is a dangerous place to live, not because of the people who are evil, but because of the people who don't do anything about it.","Albert Einstein","dangerous,evil,people,world" +"Courage is resistance to fear, mastery of fear - not absence of fear.","Mark Twain","courage" +"If we couldn't laugh we would all go insane.","Robert Frost","laughter" +"You can have it all. Just not all at once.","Oprah Winfrey","dreams,life,time" +"The best thing to hold onto in life is each other.","Audrey Hepburn","inspirational" +"Remembering that you are going to die is the best way I know to avoid the trap of thinking you have something to lose. You are already naked. There is no reason not to follow your heart.","Steve Jobs","2740-likes" +"If A is a success in life, then A equals x plus y plus z. Work is x; y is play; and z is keeping your mouth shut","Albert Einstein","albert,einstein,equations,success" +"Black holes are where God divided by zero.","Albert Einstein","humor" +"If I’m honest I have to tell you I still read fairy-tales and I like them best of all.","Audrey Hepburn","fairy-tales" +"Learn from the mistakes of others. You can never live long enough to make them all yourself.","Groucho Marx","attributed-no-source" +"Your time is limited, so don't waste it living someone else's life. Don't be trapped by dogma - which is living with the results of other people's thinking. Don't let the noise of others' opinions drown out your own inner voice. And most important, have the courage to follow your heart and intuition.","Steve Jobs","courage,time,heart" +"You can only become truly accomplished at something you love. Don’t make money your goal. Instead pursue the things you love doing and then do them so well that people can’t take their eyes off of you.","Maya Angelou","2637-likes" +"Everyone is a moon, and has a dark side which he never shows to anybody.","Mark Twain","darkness,human-nature,moon" +"Change will not come if we wait for some other person, or if we wait for some other time. We are the ones we've been waiting for. We are the change that we seek.","Barack Obama","change,inspirational" +"It's not the size of the dog in the fight, it's the size of the fight in the dog.","Mark Twain","bravery,dog,famous,inspirational,valour" +"Kindness is a language which the deaf can hear and the blind can see.","Mark Twain","inspirational,kindness" +"There's nowhere you can be that isn't where you're meant to be...","John Lennon","beatles,destiny,fate,life" +"Never make someone a priority when all you are to them is an option.","Maya Angelou","2570-likes" +"Nothing in the world is more dangerous than sincere ignorance and conscientious stupidity.","Martin Luther King Jr.","ignorance" +"The ultimate measure of a man is not where he stands in moments of comfort and convenience, but where he stands at times of challenge and controversy.","Martin Luther King Jr.","challanges" +"If there is any one secret of success, it lies in the ability to get the other person's point of view and see things from that person's angle as well as from your own.","Henry Ford","ability,view,point" +"Forgive, O Lord, my little jokes on TheeAnd I'll forgive Thy great big one on me.","Robert Frost","humor,poetry,religion" +"Procrastination is one of the most common and deadliest of diseases and its toll on success and happiness is heavy.","Wayne Gretzky","happiness,heavy,diseases" +"Humor is mankind's greatest blessing.","Mark Twain","humor" +"I've lived through some terrible things in my life, some of which actually happened.","Mark Twain","humor" +"Tell me and I forget, teach me and I may remember, involve me and I learn.","Benjamin Franklin","learning,mentoring,parenting,teacher" +"The best way to cheer yourself is to cheer somebody else up.","Albert Einstein","2471-likes" +"Lack of direction, not lack of time, is the problem. We all have twenty-four hour days.","Zig Ziglar","attention,direction,focus,inspirational,motivational" +"The secret to humor is surprise.","Aristotle","humor,surprise" +"Name the greatest of all inventors. Accident.","Mark Twain","inventing-humor" +"Behind every beautiful thing, there's some kind of pain.","Bob Dylan","beautiful,behind,kind,of,pain,thing" +"Everything must be made as simple as possible. But not simpler.","Albert Einstein","einstein,paraphrased,science,systems" +"Humor is the most engaging cowardice.","Robert Frost","cowardice,engaging" +"Try to be a rainbow in someone's cloud.","Maya Angelou","cloud,rainbow" +"You believe in a book that has talking animals, wizards, witches, demons, sticks turning into snakes, burning bushes, food falling from the sky, people walking on water, and all sorts of magical, absurd and primitive stories, and you say that we are the ones that need help?","Mark Twain","books-bible" +"The best way to cheer yourself is to try to cheer someone else up.","Mark Twain","happiness" +"When you trip over love, it is easy to get up. But when you fall in love, it is impossible to stand again.","Albert Einstein","love" +"Being the richest man in the cemetery doesn't matter to me. Going to bed at night saying we've done something wonderful... that's what matters to me.","Steve Jobs","2356-likes" +"I never forget a face, but in your case I'll be glad to make an exception.","Groucho Marx","humor" +"You may encounter many defeats, but you must not be defeated. In fact, it may be necessary to encounter the defeats, so you can know who you are, what you can rise from, how you can still come out of it.","Maya Angelou","adversity,character,failure,perseverance" +"Parents can only give good advice or put them on the right paths, but the final forming of a person's character lies in their own hands.","Anne Frank","growing-up,innocence,inspirational,parenting,personal-responsibility,right-of-passage,self-determination,self-responsibility" +"Humor is reason gone mad.","Groucho Marx","reason,mad,gone" +"What is right is not always popular and what is popular is not always right.","Albert Einstein","2280-likes" +"Freedom lies in being bold.","Robert Frost","boldness,courage,freedom" +"Be thankful for what you have; you'll end up having more. If you concentrate on what you don't have, you will never, ever have enough","Oprah Winfrey","appreciation,contentment,dissatisfaction,life,perception,positivity,thankfulness" +"Education: the path from cocky ignorance to miserable uncertainty.","Mark Twain","education" +"If you would be loved, love, and be loveable.","Benjamin Franklin","loved" +"If I am not good to myself, how can I expect anyone else to be good to me?","Maya Angelou","2202-likes" +"It does not matter how slowly you go as long as you do not stop.","Confucius","education,inspirational,life,perseverance" +"Ask for what you want and be prepared to get it!","Maya Angelou","inspirational" +"You alone are enough. You have nothing to prove to anybody.","Maya Angelou","2179-likes" +"I was born with an enormous need for affection, and a terrible need to give it.","Audrey Hepburn","born,affection" +"The easy confidence with which I know another man's religion is folly teaches me to suspect that my own is also.","Mark Twain","humor,philosophy,religion" +"Adventure is worthwhile in itself.","Amelia Earhart","2126-likes" +"Intelligence plus character-that is the goal of true education.","Martin Luther King Jr.","education" +"If everyone demanded peace instead of another television set, then there'd be peace.","John Lennon","activism,beatles,materialism,peace,society,television" +"The pursuit of truth and beauty is a sphere of activity in which we are permitted to remain children all our lives.","Albert Einstein","wonder" +"In the middle of difficulty lies opportunity","Albert Einstein","2072-likes" +"The best way out is always through.","Robert Frost","best" +"Whoever is careless with the truth in small matters cannot be trusted with important matters","Albert Einstein","character,honesty,integrity" +"I believe in God, but not as one thing, not as an old man in the sky. I believe that what people call God is something in all of us. I believe that what Jesus and Mohammed and Buddha and all the rest said was right. It's just that the translations have gone wrong.","John Lennon","god,organized-religion,religion,spirituality" +"I believe in being strong when everything seems to be going wrong. I believe that happy girls are the prettiest girls. I believe that tomorrow is another day, and I believe in miracles","Audrey Hepburn","life" +"Men marry women with the hope they will never change. Women marry men with the hope they will change. Invariably they are both disappointed.","Albert Einstein","battle-of-the-sexes,gender-stereotypes,marriage,men-and-women" +"Stepping onto a brand-new path is difficult, but not more difficult than remaining in a situation, which is not nurturing to the whole woman.","Maya Angelou","change,inspirational,self-determination" +"Poetry is what gets lost in translation.","Robert Frost","poetry" +"You know you are on the road to success if you would do your job, and not be paid for it.","Oprah Winfrey","road,paid" +"The secret of life is honesty and fair dealing. If you can fake that, you've got it made.","Groucho Marx","humor,life" +"The desire to reach for the stars is ambitious. The desire to reach hearts is wise.","Maya Angelou","ambition,inspirational,wisdom" +"Peace cannot be kept by force; it can only be achieved by understanding.","Albert Einstein","force,peace" +"Half the world is composed of people who have something to say and can't, and the other half who have nothing to say and keep on saying it.","Robert Frost","new-england-wisdom" +"He who knows all the answers has not been asked all the questions.","Confucius","wisdom" +"The secret to getting ahead is getting started.","Mark Twain","starting,working" +"A question that sometimes drives me hazy: am I or are the others crazy?","Albert Einstein","crazy,question,thought-provoking" +"If I'm the people's poet, then I ought to be in people's hands - and, I hope, in their heart.","Maya Angelou","heart,hands,ought" +"Living is Easy with Eyes Closed.","John Lennon","beatles,blindness,ignorance,life" +"If you make a mistake and do not correct it, this is called a mistake.","Confucius","inspirational,mistake" +"Happiness depends upon ourselves.","Aristotle","happiness" +"Two roads diverged in a wood, and I -I took the one less traveled by,And that has made all the difference.","Robert Frost","choice,consequence,individuality,inspirational" +"You can't blame gravity for falling in love.","Albert Einstein","gravity,blame" +"All you need in this life is ignorance and confidence; then success is sure.","Mark Twain","humor,success" +"The best way to not feel hopeless is to get up and do something. Don’t wait for good things to happen to you. If you go out and make some good things happen, you will fill the world with hope, you will fill yourself with hope.","Barack Obama","determination,helping-others,hope,initiative,just-do-it,proactivity,service,volunteerism,work" +"Imagination is everything. It is the preview of life's coming attractions.","Albert Einstein","creativity,fantasy,imagination,inspirational" +"Any book that helps a child to form a habit of reading, to make reading one of his deep and continuing needs, is good for him.","Maya Angelou","books,inspirational,library" +"The highest use of capital is not to make more money, but to make money do more for the betterment of life.","Henry Ford","money,highest,betterment" +"Educating the mind without educating the heart is no education at all.","Aristotle","education,ethics" +"It's hard to beat a person who never gives up.","Babe Ruth","hard-work,inspiration,teamwork" +"Life is really simple, but we insist on making it complicated.","Confucius","simple,insist" +"Love is composed of a single soul inhabiting two bodies.","Aristotle","bodies,soul,single" +"There is probably a perverse pride in my administration... that we were going to do the right thing, even if short-term it was unpopular. And I think anybody who's occupied this office has to remember that success is determined by an intersection in policy and politics and that you can't be neglecting of marketing and P.R. and public opinion.","Barack Obama","politics,pride,marketing" +"It is the supreme art of the teacher to awaken joy in creative expression and knowledge.","Albert Einstein","teacher,knowledge,joy" +"I am not a teacher, but an awakener.","Robert Frost","carpe-diem,education,inspirational,learning,mentoring" +"Excellence is an art won by training and habituation. We do not act rightly because we have virtue or excellence, but we rather have those because we have acted rightly. We are what we repeatedly do. Excellence, then, is not an act but a habit.","Aristotle","training,excellence,act" +"No matter what happens, or how bad it seems today, life does go on, and it will be better tomorrow.","Maya Angelou","inspirational-quotes" +"Because paper has more patience than people.","Anne Frank","affirming,inspirational,uplifting" +"Politics is the art of looking for trouble, finding it everywhere, diagnosing it incorrectly and applying the wrong remedies.","Groucho Marx","politics,wrong,looking" +"Happiness makes up in height for what it lacks in length.","Robert Frost","happiness" +"Each person must live their life as a model for others.","Rosa Parks","model,others" +"If the facts don't fit the theory, change the facts.","Albert Einstein","facts,humor,science" +"The important thing is not to stop questioning. Curiosity has its own reason for existing.","Albert Einstein","curiosity,philosophy,wonder" +"What I know is, is that if you do work that you love, and the work fulfills you, the rest will come.","Oprah Winfrey","work,rest,fulfills" +"Eating is so intimate. It's very sensual. When you invite someone to sit at your table and you want to cook for them, you're inviting a person into your life.","Maya Angelou","food,eating,table" +"A clear conscience is the sure sign of a bad memory.","Mark Twain","conscience,humor,memory" +"I must be willing to give up what I am in order to become what I will be.","Albert Einstein","inspirational" +"I haven't any right to criticize books, and I don't do it except when I hate them. I often want to criticize Jane Austen, but her books madden me so that I can't conceal my frenzy from the reader; and therefore I have to stop every time I begin. Every time I read Pride and Prejudice I want to dig her up and beat her over the skull with her own shin-bone.","Mark Twain","austen,criticism,reading,writing" +"I hope the millions of people I've touched have the optimism and desire to share their goals and hard work and persevere with a positive attitude.","Michael Jordan","attitude,positive,work" +"There is no abstract art. You must always start with something. Afterward you can remove all traces of reality.","Pablo Picasso","reality,start,abstract" +"Love is a better master than duty.","Albert Einstein","love" +"He that can have patience can have what he will.","Benjamin Franklin","determination,inspirational,patience" +"When I die, I hope to go to Heaven, whatever the Hell that is.","Ayn Rand","death,hell,heaven" +"Death is no more than passing from one room into another. But there's a difference for me, you know. Because in that other room I shall be able to see.","Helen Keller","blindness,death,faith,inspirational,wisdom" +"On every front there are clear answers out there that can make this country stronger, but we're going to break through the fear and the frustration people are feeling. Our job is to make sure that even as we make progress, that we are also giving people a sense of hope and vision for the future.","Barack Obama","future,fear,progress" +"The biggest adventure you can ever take is to live the life of your dreams.","Oprah Winfrey","adventure,bravery,inspirational,life" +"Why do they always teach us that it's easy and evil to do what we want and that we need discipline to restrain ourselves? It's the hardest thing in the world--to do what we want. And it takes the greatest kind of courage. I mean, what we really want.","Ayn Rand","career,inspiration,life" +"No one really knows why they are alive until they know what they'd die for.","Martin Luther King Jr.","death,inspirational" +"A banker is a fellow who lends you his umbrella when the sun is shining, but wants it back the minute it begins to rain.","Mark Twain","bank,bankers,humor" +"Happiness is the meaning and the purpose of life, the whole aim and end of human existence.","Aristotle","happiness" +"A Penny Saved is a Penny Earned","Benjamin Franklin","funny,inspirational,money" +"If someone thinks that peace and love are just a cliche that must have been left behind in the 60s, that's a problem. Peace and love are eternal.","John Lennon","1960s,beatles,love,peace,sixties" +"It would be possible to describe everything scientifically, but it would make no sense; it would be without meaning, as if you described a Beethoven symphony as a variation of wave pressure.","Albert Einstein","art,cybernetics,science" +"Your time is limited, so don't waste it living someone else's life. Don't be trapped by dogma - which is living with the results of other people's thinking. Don't let the noise of other's opinions drown out your own inner voice. And most important, have the courage to follow your heart and intuition. They somehow already know what you truly want to become. Everything else is secondary.","Steve Jobs","death,life,r-i-p,steve" +"The most important thing is to enjoy your life - to be happy - it's all that matters.","Audrey Hepburn","happy,enjoy,matters" +"Those who are not looking for happiness are the most likely to find it, because those who are searching forget that the surest way to be happy is to seek happiness for others.","Martin Luther King Jr.","happiness" +"And God help Bruce Springsteen when they decide he's no longer God... They'll turn on him, and I hope he survives it.","John Lennon","god,help,turn" +"There's a world of difference between truth and facts. Facts can obscure truth.","Maya Angelou","truth" +"Excellence is never an accident. It is always the result of high intention, sincere effort, and intelligent execution; it represents the wise choice of many alternatives - choice, not chance, determines your destiny.","Aristotle","choice,inspirational" +"If a man is called to be a street sweeper, he should sweep streets even as a Michaelangelo painted, or Beethoven composed music or Shakespeare wrote poetry. He should sweep streets so well that all the hosts of heaven and earth will pause to say, 'Here lived a great street sweeper who did his job well.","Martin Luther King Jr.","excellence,inspirational" +"Once I knew only darkness and stillness... my life was without past or future... but a little word from the fingers of another fell into my hand that clutched at emptiness, and my heart leaped to the rapture of living.","Helen Keller","life,future,emptiness" +"In wine there is wisdom, in beer there is Freedom, in water there is bacteria.","Benjamin Franklin","drinking,humor" +"A man is a success if he gets up in the morning and gets to bed at night, and in between he does what he wants to do.","Bob Dylan","bob-dylan,music,success" +"I didn't have time to write a short letter, so I wrote a long one instead.","Mark Twain","humor" +"The smallest minority on earth is the individual. Those who deny individual rights cannot claim to be defenders of minorities.","Ayn Rand","freedom-of-thought,philosophy,politics,rights" +"The way a team plays as a whole determines its success. You may have the greatest bunch of individual stars in the world, but if they don't play together, the club won't be worth a dime.","Babe Ruth","sports,team,together" +"Those are my principles, and if you don't like them...well I have others.","Groucho Marx","humor" +"Home is the place where, when you have to go there, They have to take you in.","Robert Frost","inspirational-quotes" +"Many people die at twenty five and aren't buried until they are seventy five.","Benjamin Franklin","death,life,wordplay" +"Fear not death for the sooner we die, the longer we shall be immortal.","Benjamin Franklin","death" +"I never made one of my discoveries through the process of rational thinking","Albert Einstein","creativity,epiphany,humor,inspiration" +"Any emotion, if it is sincere, is involuntary.","Mark Twain","inspirational" +"I must have a prodigious amount of mind; it takes me as much as a week, sometimes, to make it up!","Mark Twain","decisions,humor,mind" +"The two most important days in your life are the day you are born and the day you find out why.","Mark Twain","birth,born,day,important,life,reasons-why,true" +"Once we accept our limits, we go beyond them.","Albert Einstein","inspirational,self-improvement,self-knowledge" +"Alone we can do so little; together we can do so much","Helen Keller","collaboration,life,teamwork" +"One morning I shot an elephant in my pajamas. How he got in my pajamas I'll never know.","Groucho Marx","humor" +"The best revenge is massive success.","Frank Sinatra","best,revenge,massive" +"My favorite things in life don't cost any money. It's really clear that the most precious resource we all have is time.","Steve Jobs","time,money,precious" +"We, the People, recognize that we have responsibilities as well as rights; that our destinies are bound together; that a freedom which only asks what's in it for me, a freedom without a commitment to others, a freedom without love or charity or duty or patriotism, is unworthy of our founding ideals, and those who died in their defense.","Barack Obama","patriotism,freedom,died" +"The rain to the wind said,You push and I'll pelt.'They so smote the garden bedThat the flowers actually knelt,And lay lodged--though not dead.I know how the flowers felt.","Robert Frost","poetry,rain" +"Our goals can only be reached through a vehicle of a plan, in which we must fervently believe, and upon which we must vigorously act. There is no other route to success.","Pablo Picasso","goals,plan,act" +"To perceive is to suffer.","Aristotle","empathy,life,suffer,understanding" +"The funniest people are the saddest ones","Confucius","funny,people,sad" +"I didn't fail the test, I just found 100 ways to do it wrong.","Benjamin Franklin","examinations,failure,humor" +"Your time is limited, so don't waste it living someone else's life.","Steve Jobs","life,life-and-living" +"Don't ever take a fence down until you know why it was put up.","Robert Frost","advice,caution,cautionary,inspirational" +"Life is a series of experiences, each one of which makes us bigger, even though sometimes it is hard to realize this. For the world was built to develop character, and we must learn that the setbacks and grieves which we endure help us in our marching onward.","Henry Ford","learning,setbacks" +"Government exists to protect us from each other. Where government has gone beyond its limits is in deciding to protect us from ourselves.","Ronald Reagan","freedom-of-choice,government,inspirational,political-philosophy,politics" +"Be slow in choosing a friend, slower in changing.","Benjamin Franklin","friend,slow,changing" +"If a man wants you, nothing can keep him away. If he doesn't want you, nothing can make him stay.","Oprah Winfrey","romance" +"Well, art is art, isn't it? Still, on the other hand, water is water! And east is east and west is west and if you take cranberries and stew them like applesauce they taste much more like prunes than rhubarb does. Now, uh... now you tell me what you know.","Groucho Marx","art,humor" +"The educated differ from the uneducated as much as the living differ from the dead.","Aristotle","education" +"When we remember we are all mad, the mysteries disappear and life stands explained.","Mark Twain","remember,mad,explained" +"I used to think anyone doing anything weird was weird. Now I know that it is the people that call others weird that are weird.","Paul McCartney","beatles,humor,music,nonjudgemental" +"I have a dream that my four little children will one day live in a nation where they will not be judged by the color of their skin but by the content of their character.","Martin Luther King Jr.","character,dream,inspirational,prejudice,race" +"Imagination is the highest form of research.","Albert Einstein","inspiration" +"Wherever you go, go with all your heart.","Confucius","confucius,inspirational" +"Life is one grand, sweet song, so start the music.","Ronald Reagan","music,song,start" +"We must live together as brothers or perish together as fools.","Martin Luther King Jr.","brotherhood,inspirational,wisdom" +"If a black cat crosses your path, it signifies that the animal is going somewhere.","Groucho Marx","humor,superstition" +"Science investigates; religion interprets. Science gives man knowledge, which is power; religion gives man wisdom, which is control. Science deals mainly with facts; religion deals mainly with values. The two are not rivals.","Martin Luther King Jr.","religion,science" +"I heard a definition once: Happiness is health and a short memory! I wish I'd invented it, because it is very true.","Audrey Hepburn","happiness,inspirational" +"The most incomprehensible thing about the world is that it is at all comprehensible.","Albert Einstein","einstein,philosophy,science" +"I sustain myself with the love of family.","Maya Angelou","family,inspirational" +"Some people claim that marriage interferes with romance. There's no doubt about it. Anytime you have a romance, your wife is bound to interfere.","Groucho Marx","marriage,romance,sex" +"Learn the rules like a pro, so you can break them like an artist.","Pablo Picasso","art,creativity,motivational,picasso,rules,writing" +"I made my first white women friends in college; they loved me and were loyal to our friendship, but I understood, as they did, that they were white women and that whiteness mattered.","Alice Walker","women,college,loved" +"I've come to believe that each of us has a personal calling that's as unique as a fingerprint - and that the best way to succeed is to discover what you love and then find a way to offer it to others in the form of service, working hard, and also allowing the energy of the universe to lead you.","Oprah Winfrey","individuality,inspirational,success" +"Everyone here has the sense that right now is one of those moments when we are influencing the future.","Steve Jobs","future,moments" +"I trust that everything happens for a reason, even if we are not wise enough to see it.","Oprah Winfrey","inspirational" +"Anyone who stops learning is old, whether at twenty or eighty. Anyone who keeps learning stays young.","Henry Ford","education,lifelong-learning" +"All I can be is me- whoever that is.","Bob Dylan","bob-dylan,inspirational" +"The Bible has noble poetry in it... and some good morals and a wealth of obscenity, and upwards of a thousand lies.","Mark Twain","agnosticism,atheism,bible,christianity,mythology,truth" +"Only those who attempt the absurd can achieve the impossible.","Albert Einstein","achievement,inspirational,optimism" +"Limitless undying love which shines around me like a million suns it calls me on and on across the universe.","John Lennon","beatles,love,sun,universe" +"The people who make art their business are mostly imposters.","Pablo Picasso","business,mostly" +"First best is falling in love. Second best is being in love. Least best is falling out of love. But any of it is better than never having been in love.","Maya Angelou","falling-in-love,love,lovelessness" +"Breathe. Let go. And remind yourself that this very moment is the only one you know you have for sure.","Oprah Winfrey","life" +"I'm happy to be a writer - of prose, poetry, every kind of writing. Every person in the world who isn't a recluse, hermit or mute uses words. I know of no other art form that we always use.","Maya Angelou","poetry,happy,words" +"The dog is a gentleman; I hope to go to his heaven not man's.","Mark Twain","animals,dogs,heaven,inspirational,man,religion" +"Every strike brings me closer to the next home run.","Babe Ruth","baseball,perseverance,philosophy,sports" +"As my sufferings mounted I soon realized that there were two ways in which I could respond to my situation -- either to react with bitterness or seek to transform the suffering into a creative force. I decided to follow the latter course.","Martin Luther King Jr.","activism,healing,inspirational,justice,sacrifice" +"Don’t let the noise of others’ opinions drown out your own inner voice."[Stanford University commencement speech, 2005]","Steve Jobs","apple-computer-inc,identity,independence,inner-voice,life,steve-jobs" +"When we give cheerfully and accept gratefully, everyone is blessed.","Maya Angelou","charity,compassion,gratitude,helping-others,inspiration" +"Reading, after a certain age, diverts the mind too much from its creative pursuits. Any man who reads too much and uses his own brain too little falls into lazy habits of thinking.","Albert Einstein","reading,science" +"There are two kinds of teachers: the kind that fill you with so much quail shot that you can't move, and the kind that just gives you a little prod behind and you jump to the skies.","Robert Frost","inspirational,teachers" +"Men should think twice before making widowhood women's only path to power.","Gloria Steinem","death,feminism,men,murder,widowhood,women,womens-rights" +"To be a poet is a condition, not a profession.","Robert Frost","afflictions,on-writing,poetry" +"How many observe Christ's birthday! How few, His precepts!","Benjamin Franklin","christian-behavior,christmas,religion" +"He is free to evade reality, he is free to unfocus his mind and stumble blindly down any road he pleases, but not free to avoid the abyss he refuses to see.","Ayn Rand","inspirational" +"Memories of our lives, of our works and our deeds will continue in others.","Rosa Parks","memories,others" +"Do not worry about your difficulties in Mathematics. I can assure you mine are still greater.","Albert Einstein","inspirational" +"Life is either a great adventure or nothing.","Helen Keller","great,adventure" +"Hide not your talents, they for use were made,What's a sundial in the shade?","Benjamin Franklin","talent,wisdom,work" +"Student: Dr. Einstein, Aren't these the same questions as last year's [physics] final exam?Dr. Einstein: Yes; But this year the answers are different.","Albert Einstein","humor,science" +"You cannot open a book without learning something.","Confucius","books,learning,philosopher" +"It takes a very long time to become young.","Pablo Picasso","ignorance,innocence,knowledge,wisdom,youth" +"Without leaps of imagination or dreaming, we lose the excitement of possibilities. Dreaming, after all is a form of planning.","Gloria Steinem","dreaming,dreams,excitement,imagination,inspirational,planning,possibility" +"A Woman in harmony with her spirit is like a river flowing.She goes where she will without pretense and arrives at her destination prepared to be herself and only herself","Maya Angelou","inspirational" +"We can't help everyone, but everyone can help someone.","Ronald Reagan","help" +"Wisdom is not a product of schooling but of the lifelong attempt to acquire it.","Albert Einstein","education,knowledge-wisdom,learning,school,science,wisdom" +"Well done is better than well said.","Benjamin Franklin","inspirational" +"Anyone who says he can see through women is missing a lot.","Groucho Marx","funny,innuendo" +"What the superior man seeks is in himself; what the small man seeks is in others.","Confucius","individuality,inspiration,validation" +"Surround yourself only with people who are going to take you higher.","Oprah Winfrey","inspirational" +"Whoever is happy will make others happy too.","Anne Frank","happy,others,whoever" +"Sometimes it's not enough to know what things mean, sometimes you have to know what things don't mean.","Bob Dylan","understanding,wisdom" +"When you see a good person, think of becoming like her/him. When you see someone not so good, reflect on your own weak points.","Confucius","helpful,inspirational,introspection,karma,self-improvement" +"If we ever forget that we're one nation under God, then we will be one nation gone under.","Ronald Reagan","inspirational,religon,separation-of-church-and-state" +"Close your eyes and I'll kiss you, Tomorrow I'll miss you.","Paul McCartney","beatles,kiss,love,miss-you,music,song-lyrics,tomorrow" +"We all live with the objective of being happy; our lives are all different and yet the same.","Anne Frank","happiness,literature" +"the time is always right to do the right thing","Martin Luther King Jr.","inspirational" +"Rich people have small TVs and big libraries, and poor people have small libraries and big TVs.","Zig Ziglar","intelligence,success,wealth" +"Deliver me from writers who say the way they live doesn't matter. I'm not sure a bad person can write a good book. If art doesn't make us better, then what on earth is it for.","Alice Walker","art,bad,good,writing" +"I've been lucky. Opportunities don't often come along. So, when they do, you have to grab them.","Audrey Hepburn","inspirational" +"The truth is not for all men but only for those who seek it.","Ayn Rand","truth" +"God is a concept by which we measure our pain.","John Lennon","beatles,god,religion" +"How can I go forward when I don't know which way I'm facing?","John Lennon","life" +"Love is a promise, love is a souvenir, once given never forgotten, never let it disappear.","John Lennon","attributed-no-source,love" +"Two roads diverged in a wood and I - I took the one less traveled by, and that has made all the difference.","Robert Frost","roads,difference" +"I'm not afraid of death because I don't believe in it.It's just getting out of one car, and into another.","John Lennon","death,spirituality" +"If there is any religion that could respond to the needs of modern science, it would be Buddhism.","Albert Einstein","attributed,attributed-to-einstein-no-source,buddhism,religion,science,unsourced" +"Instead of cursing the darkness, light a candle.","Benjamin Franklin","inspirational,proverb" +"Freedom is never more than one generation away from extinction. We didn't pass it to our children in the bloodstream. It must be fought for, protected, and handed on for them to do the same, or one day we will spend our sunset years telling our children and our children's children what it was once like in the United States where men were free.","Ronald Reagan","freedom,inspirational,personal-freedom" +"I am always doing that which I can not do, in order that I may learn how to do it.","Pablo Picasso","inspirational" +"He who hath many friends hath none.","Aristotle","hath,none" +"When you encourage others, you in the process are encouraged because you're making a commitment and difference in that person's life. Encouragement really does make a difference.","Zig Ziglar","commitment,process" +"Few people are capable of expressing with equanimity opinions which differ from the prejudices of their social enviroment. Most people are incapable of forming such opinions."(Essay to Leo Baeck, 1953)","Albert Einstein","disagreement,dissent,equanimity,expressing,knowledge,opinions,prejudices,social,society,thought" +"If you live each day as it was your last, someday you'll most certainly be right","Steve Jobs","death,life,rip,steve" +"Happy girls are the prettiest","Audrey Hepburn","attractiveness,beauty,happiness" +"All great achievements require time.","Maya Angelou","inspirational,motivation" +"Some painters transform the sun into a yellow spot, others transform a yellow spot into the sun.","Pablo Picasso","sun,yellow,others" +"I want to thank you, Lord, for life and all that's in it. Thank you for the day and for the hour, and the minute.","Maya Angelou","inspirational" +"Eat a live frog first thing in the morning and nothing worse will happen to you the rest of the day.","Mark Twain","humor" +"On some positions, cowardice asks the question, is it expedient? And then expedience comes along and asks the question, is it politic? Vanity asks the question, is it popular? Conscience asks the question, is it right?There comes a time when one must take the position that is neither safe nor politic nor popular, but he must do it because conscience tells him it is right.","Martin Luther King Jr.","truth" +"I cannot imagine a God who rewards and punishes the objects of his creation, whose purposes are modeled after our own -- a God, in short, who is but a reflection of human frailty. Neither can I believe that the individual survives the death of his body, although feeble souls harbor such thoughts through fear or ridiculous egotisms.","Albert Einstein","god,rational,religion,thinking" +"Nothing will work unless you do.","Maya Angelou","inspirational" +"The heart can think of no devotionGreater than being shore to the ocean-Holding the curve of one position,Counting an endless repetition.","Robert Frost","devotion,love,poetry" +"Just give me a comfortable couch, a dog, a good book, and a woman. Then if you can get the dog to go somewhere and read the book, I might have a little fun.","Groucho Marx","dogs,humor,innuendo,naughty" +"If all printers were determined not to print anything till they were sure it would offend nobody, there would be very little printed.","Benjamin Franklin","books,censorship,reading" +"I love the song 'I Hope You Dance' by Lee Ann Womack. I was going to write that song, but someone beat me to it.","Maya Angelou","love,dance,song" +"The way to know life is to love many things.","Vincent Van Gogh","life" +"I believe in intuitions and inspirations...I sometimes FEEL that I am right. I do not KNOW that I am.","Albert Einstein","certainty,faith,intuition" +"Yes We Can!","Barack Obama","action,hope,positivity" +"I've had a lot of worries in my life, most of which never happened.","Mark Twain","inspirational,positive,worries" +"Never bend your head. Hold it high. Look the world straight in the eye.","Helen Keller","inspirational" +"I long to accomplish a great and noble task, but it is my chief duty to accomplish small tasks as if they were great and noble.","Helen Keller","inspirational" +"Life is more or less a lie, but then again, that's exactly the way we want it to be.","Bob Dylan","dylan,life,zen" +"The high-minded man must care more for the truth than for what people think.","Aristotle","truth" +"My religion consists of a humble admiration of the illimitable superior spirit who reveals himself in the slight details we are able to perceive with our frail and feeble mind.","Albert Einstein","god,religion,spirit,spirituality" +"It is not the failure of others to appreciate your abilities that should trouble you, but rather your failure to appreciate theirs.","Confucius","inspirational" +"The man of wisdom is never of two minds;the man of benevolence never worries;the man of courage is never afraid.","Confucius","wisdom" +"Success is the maximum utilization of the ability that you have.","Zig Ziglar","ability,maximum" +"I've learned that you shouldn't go through life with a catcher's mitt on both hands; you need to be able to throw some things back.","Maya Angelou","wisdom" +"If at first the idea is not absurd, then there is no hope for it.","Albert Einstein","absurd,absurdity,hope,idea" +"Love is the only force capable of transforming an enemy to a friend.","Martin Luther King Jr.","love" +"Simple can be harder than complex: You have to work hard to get your thinking clean to make it simple. But it’s worth it in the end because once you get there, you can move mountains.","Steve Jobs","complex,hard,simple,thinking,truth,wisdom,worth" +"The way out is through the door. Why is it that no one will use this method?","Confucius","tao,wisdom,zen" +"Stay hungry. Stay foolish.","Steve Jobs","inspirational" +"When you do something noble and beautiful and nobody noticed, do not be sad. For the sun every morning is a beautiful spectacle and yet most of the audience still sleeps.","John Lennon","beatles,beauty,life,nature,sadness" +"I have left orders to be awakened at any time during national emergency, even if I'm in a cabinet meeting.","Ronald Reagan","growing-older,humor,napping,politics" +"The tragedy of life is what dies inside a man while he lives.","Albert Einstein","inspirational,life" +"One thing I have learned in a long life: that all our science, measured against reality, is primitive and childlike -- and yet it is the most precious thing we have.","Albert Einstein","science" +"He not busy being born is busy dying.","Bob Dylan","inspirational,lyrics,music,self-discovery,songs" +"Earl Nightingale has inspired more people toward success and fortune than any other motivational speaker on the planet.","Zig Ziglar","inspired,planet,fortune" +"People don’t like to think, if one thinks, one must reach conclusions. Conclusions are not always pleasant.","Helen Keller","life,thinking" +"Life is a whim of several billion cells to be you for a while","Groucho Marx","humor" +"The first question which the priest and the Levite asked was: 'If I stop to help this man, what will happen to me?' But...the good Samaritan reversed the question: 'If I do not stop to help this man, what will happen to him?","Martin Luther King Jr.","love,neighbor,sacrifice,service" +"In all the world, there is no heart for me like yours.In all the world, there is no love for you like mine.","Maya Angelou","love,perfect-fit" +"Give a bowl of rice to a man and you will feed him for a day. Teach him how to grow his own rice and you will save his life.","Confucius","education,enduring-good,generosity,life,wise-advice,work" +"The Revolution introduced me to art, and in turn, art introduced me to the Revolution!","Albert Einstein","communism,democracy,freedom,history,hope,individualism,philosophy,politics,revolution,socialism" +"I have gained this by philosophy … I do without being ordered what some are constrained to do by their fear of the law.","Aristotle","law,morality,philosophy,secular-ethics,secular-morality" +"Intellectual growth should commence at birth and cease only at death.","Albert Einstein","inspirational,lifelong-learning" +"The hardest thing of all is to find a black cat in a dark room, especially if there is no cat.","Confucius","futility,knowledge,religion" +"Only the wisest and stupidest of men never change.","Confucius","wisdom" +"Part of me suspects that I'm a loser, and the other part of me thinks I'm God Almighty.","John Lennon","ego,god,life,loser" +"Sometimes life hits you in the head with a brick. Don't lose faith.","Steve Jobs","faith,brick,head" +"We know from daily life that we exist for other people first of all, for whose smiles and well-being our own happiness depends.","Albert Einstein","friendship,life,relationships" +"Don't find fault, find a remedy; anybody can complain","Henry Ford","common-sense,inspirational" +"Your success and happiness lies in you. Resolve to keep happy, and your joy and you shall form an invincible host against difficulties.","Helen Keller","new-year's,happiness" +"What I'm looking for is not out there, it is in me.","Helen Keller","self,truth" +"There is no limit to the amount of good you can do if you don't care who gets the credit.","Ronald Reagan","accomplishment,achievement,inspirational,misattributed,modesty,recognition" +"I was married by a judge. I should have asked for a jury.","Groucho Marx","humor,marriage" +"Even on the most solemn occasions I got away without wearing socks and hid that lack of civilization in high boots","Albert Einstein","fashion,humor" +"We dance round in a ring and suppose,But the Secret sits in the middle and knows.","Robert Frost","inspirational" +"The most beautiful thing we can experience is the mysterious. It is the source of all true art and all science. He to whom this emotion is a stranger, who can no longer pause to wonder and stand rapt in awe, is as good as dead: his eyes are closed.","Albert Einstein","art,awe,einstein,emotion,eyes,life,mysterious,reality,science,the-mysterious,truth,vision" +"The ideals which have always shone before me and filled me with joy are goodness, beauty, and truth.","Albert Einstein","beauty,ideas,kindness,truth" +"The arc of the moral universe is long, but it bends towards justice.","Martin Luther King Jr.","inspirational,justice,mlk" +"If I cannot do great things, I can do small things in a great way","Martin Luther King Jr.","excellence,success" +"You don't need anybody to tell you who you are or what you are. You are what you are!","John Lennon","be-yourself,inspirational,self-awareness,self-determination" +"The word 'God' is for me nothing more than the expression and product of human weaknesses, the Bible a collection of honorable, but still primitive legends which are nevertheless pretty childish. No interpretation, no matter how subtle, can (for me) change this.","Albert Einstein","bible,god,religion" +"You can't connect the dots looking forward; you can only connect them looking backwards. So you have to trust that the dots will somehow connect in your future.","Steve Jobs","design,inspirational,life-lessons" +"God is subtle but he is not malicious.","Albert Einstein","inspirational" +"To write well, express yourself like the common people, but think like a wise man.","Aristotle","inspirational,philosophy,writing" +"An individual has not started living until he can rise above the narrow confines of his individualistic concerns to the broader concerns of all humanity.","Martin Luther King Jr.","inspirational" +"The person who deserves most pity is a lonesome one on a rainy day who doesn't know how to read.","Benjamin Franklin","books,illiteracy,literature,pity,reading,words" +"No, this trick won't work... How on earth are you ever going to explain in terms of chemistry and physics so important a biological phenomenon as first love?","Albert Einstein","chemistry,first-love,love,physics" +"Homer has taught all other poets the art of telling lies skillfully.","Aristotle","homer,poets,lies" +"Let us live so that when we come to die even the undertaker will be sorry.","Mark Twain","inspirational" +"Next time I see you, remind me not to talk to you.","Groucho Marx","gibe,humor,insult" +"What is Man? Man is a noisome bacillus whom Our Heavenly Father created because he was disappointed in the monkey.","Mark Twain","creation,evolution,humor,mankind" +"Information is not knowledge.","Albert Einstein","information,knowledge" +"Literature is my Utopia","Helen Keller","books,literature,reading,words" +"I have looked in the mirror every morning and asked myself: "If today were the last day of my life, would I want to do what I am about to do today?"And whenever the answer has been "No"for too many days in a row, I know I need to change something.","Steve Jobs","change,life" +"We could never learn to be brave and patient if there were only joy in the world","Helen Keller","adversity,inspirational,personal-growth" +"Challenges are gifts that force us to search for a new center of gravity. Don't fight them. Just find a new way to stand.","Oprah Winfrey","philosophy" +"In the unlikely story that is America, there has never been anything false about hope.","Barack Obama","america,history,hope" +"Possessions, outward success, publicity, luxury - to me these have always been contemptible. I believe that a simple and unassuming manner of life is best for everyone, best for both the body and the mind.","Albert Einstein","belongings,simplicity,success,values" +"I believe that every single event in life happens in an opportunity to choose love over fear.","Oprah Winfrey","fear,love,opportunity" +"I'm not the smartest fellow in the world, but I can sure pick smart colleagues.","Franklin D. Roosevelt","funny,humor,politics,usa" +"Writing is the only thing that when I do it, I don't feel I should be doing something else.","Gloria Steinem","writing" +"Forget injuries, never forget kindnesses.","Confucius","life,principles" +"Happiness does not come from without, it comes from within","Helen Keller","happiness,life,love" +"A bend in the road is not the end of the road…Unless you fail to make the turn.","Helen Keller","change,life,perseverance,risk" +"Dancers are the athletes of God.","Albert Einstein","albert,life" +"You can never be wise and be in love at the same time.","Bob Dylan","bob-dylan,love" +"Room service? Send up a larger room."[A Night at the Opera]","Groucho Marx","hotel-rooms,hotels,humor,room-service" +"Under certain circumstances, profanity provides a relief denied even to prayer."[Mark Twain, a Biography]","Mark Twain","humor,prayer,profanity,relief,you-ll-completely-understand-why" +"Wit is educated insolence.","Aristotle","definitions,humor" +"Life isn't worth living, unless it is lived for someone else.","Albert Einstein","einstein,life" +"Everything is determined, the beginning as well as the end, by forces over which we have no control. It is determined for the insect, as well as for the star. Human beings, vegetables, or cosmic dust, we all dance to a mysterious tune, intoned in the distance by an invisible piper.","Albert Einstein","destiny,einstein,inspirational" +"Early to bed and early to rise makes a man healthy, wealthy, and wise.","Benjamin Franklin","inspirational" +"It is, in fact, nothing short of a miracle that the modern methods of instruction have not yet entirely strangled the holy curiosity of inquiry; for this delicate little plant, aside from stimulation, stands mainly in need of freedom. Without this it goes to wrack and ruin without fail.","Albert Einstein","creativity,education,learning,regimentation" +"Everything has its wonders, even darkness and silence, and I learn, whatever state I may be in, therein to be content","Helen Keller","life" +"I want to do it because I want to do it. Women must try to do things as men have tried. When they fail, their failure must be but a challenge to others.","Amelia Earhart","failure,success,women,work" +"I count him braver who overcomes his desires than him who overcomes his enemies.","Aristotle","ethics,philosophy,self-discovery" +"The reports of my death are greatly exaggerated.","Mark Twain","death,humor" +"Let us not seek to satisfy our thirst for freedom by drinking from the cup of bitterness and hatred.","Martin Luther King Jr.","communism,democracy,freedom,liberation,love,marxism,peace,radicalism,socialism,theology" +"A successful book is not made of what is in it, but what is left out of it.","Mark Twain","books,humor,on-writing,wit,writing" +"We ran as if to meet the moon.","Robert Frost","moon,poetry,robert-frost" +"We all know that Art is not truth. Art is a lie that makes us realize truth at least the truth that is given us to understand. The artist must know the manner whereby to convince others of the truthfulness of his lies.","Pablo Picasso","art,lies,truth" +"When I was younger, I could remember anything, whether it had happened or not; but my faculties are decaying now and soon I shall be so I cannot remember any but the things that never happened. It is sad to go to pieces like this but we all have to do it.","Mark Twain","aging,humor" +"I'm inspired by the people I meet in my travels--hearing their stories, seeing the hardships they overcome, their fundamental optimism and decency. I'm inspired by the love people have for their children. And I'm inspired by my own children, how full they make my heart. They make me want to work to make the world a little bit better. And they make me want to be a better man.","Barack Obama","family,hope,service" +"You must expect great things of yourself before you can do them.","Michael Jordan","inspirational,success" +"Many persons have a wrong idea of what constitutes true happiness. It is not attained through self-gratification but through fidelity to a worthy purpose.","Helen Keller","educator,inspirational" +"The way a crowShook down on meThe dust of snowFrom a hemlock treeHas given my heartA change of moodAnd saved some partOf a day I had rued.","Robert Frost","poetry" +"God is really only another artist. He invented the giraffe, the elephant and the cat. He has no real style, He just goes on trying other things.","Pablo Picasso","art,god" +"The thing the sixties did was to show us the possibilities and the responsibility that we all had. It wasn't the answer. It just gave us a glimpse of the possibility.","John Lennon","1960s,beatles,hope,possibility,responsibility" +"And were an epitaph to be my story I'd have a short one ready for my own. I would have written of me on my stone: I had a lover's quarrel with the world.","Robert Frost","life" +"When obstacles arise, you change your direction to reach your goal; you do not change your decision to get there.","Zig Ziglar","inspirational" +"Training is everything. The peach was once a bitter almond; cauliflower is nothing but cabbage with a college education.","Mark Twain","education,food" +"Love is like a virus. It can happen to anybody at any time.","Maya Angelou","love" +"Vision without execution is just hallucination.","Henry Ford","dreams,success" +"My life isn’t theories and formulae. It’s part instinct, part common sense. Logic is as good a word as any, and I’ve absorbed what logic I have from everything and everyone… from my mother, from training as a ballet dancer, from Vogue magazine, from the laws of life and health and nature.","Audrey Hepburn","audrey-hepburn,inspiration,life" +"Any customer can have a car painted any colour that he wants so long as it is black.","Henry Ford","black,customer,humor,model-t" +"Play is the highest form of research.","Albert Einstein","childhood,education" +"We can't become what we need to be by remaining what we are.","Oprah Winfrey","life" +"It is by the goodness of god that in our country we have those 3 unspeakably precious things: freedom of speech, freedom of conscience, and the prudence never to practice either of them.","Mark Twain","humor,politics" +"To those who are given much, much is expected.","Maya Angelou","inspirational-quotes" +"Books can not be killed by fire. People die, but books never die. No man and no force can abolish memory... In this war, we know, books are weapons. And it is a part of your dedication always to make them weapons for man's freedom.","Franklin D. Roosevelt","book-burning,books,censorship,freedom" +"Relationships are like Rome -- difficult to start out, incredible during the prosperity of the 'golden age', and unbearable during the fall. Then, a new kingdom will come along and the whole process will repeat itself until you come across a kingdom like Egypt... that thrives, and continues to flourish. This kingdom will become your best friend, your soul mate, and your love.","Helen Keller","love,relationship" +"Our society is run by insane people for insane objectives","John Lennon","truth" +"I know in my heart that man is good, that what is right will always eventually triumph, and there is purpose and worth to each and every life.","Ronald Reagan","inspirational" +"Before I speak, I have something important to say.","Groucho Marx","absurdist,humor" +"If you lose hope, somehow you lose the vitality that keeps moving, you lose that courage to be, that quality that helps you go on in spite of it all. And so today I still have a dream.","Martin Luther King Jr.","hope" +"Time flows away like the water in the river.","Confucius","wisdom" +"Self-esteem comes from being able to define the world in your own terms and refusing to abide by the judgments of others.","Oprah Winfrey","inspirational" +"An empty stomach is not a good political adviser.","Albert Einstein","humor,hunger,poverty" +"A foolish faith in authority is the worst enemy of truth.","Albert Einstein","dissent,independent-thought,science" +"My life has been one great big joke,A dance that's walked,A song that's spoke,I laugh so hard I almost choke,When I think about myself.","Maya Angelou","life" +"When red-headed people are above a certain social grade their hair is auburn.","Mark Twain","class,elitism,humor,redheads" +"A poem begins with a lump in the throat; a homesickness or a love sickness. It is a reaching-out toward expression; an effort to find fulfillment. A complete poem is one where an emotion has found its thought and the thought has found words.","Robert Frost","poetry,writing" +"Education breeds confidence. Confidence breeds hope. Hope breeds peace.","Confucius","education" +"Poetry puts starch in your backbone so you can stand, so you can compose your life.","Maya Angelou","poetry" +"It frightens me, the awful truth, of how sweet life can be...","Bob Dylan","fear,life,truth" +"Have you ever felt the longing for someone you could admire? For something, not to look down at, but up to?","Ayn Rand","achievement,admiration,atlas-shrugged,inspirational,love,objectivism" +"Politics is not a bad profession. If you succeed there are many rewards, if you disgrace yourself you can always write a book.","Ronald Reagan","humor,politics" +"Shall I teach you what knowledge?When you know a thing, say that you know it;when you do not know a thing,admit that you do not know it.That is knowledge","Confucius","knowledge" +"My best friend is the man who in wishing me well wishes it for my sake.","Aristotle","best,friend,wishes" +"Make today worth remembering.","Zig Ziglar","inspirational,motivational" +"I've heard that hard work never killed anyone, but I say why take the chance?","Ronald Reagan","humor,laziness,sloth,work" +"I was feeling insecure you might not love me anymore","John Lennon","insecure,insecurity,love" +"Democracy cannot succeed unless those who express their choice are prepared to choose wisely. The real safeguard of democracy, therefore, is education.","Franklin D. Roosevelt","education,government,political-philosophy,public-duty,voting" +"Life is very short, and there's no time for fussing and fighting my friends","John Lennon","inspirational" +"Happiness is not in the mere possession of money; it lies in the joy of achievement, in the thrill of creative effort.","Franklin D. Roosevelt","achievement,happiness,wealth,wisdom" +"Doing the best at this moment puts you in the best place for the mext moment","Oprah Winfrey","inspirational,life,success" +"The lack of money is the root of all evil.","Mark Twain","humor" +"The human spirit must prevail over technology.","Albert Einstein","humanity,science" +"Status quo, you know, is Latin for 'the mess we're in'.","Ronald Reagan","inspirational,latin,politics" +"If we bear all this suffering and if there are still Jews left, when it is over, then Jews, instead of being doomed, will be held up as an example.","Anne Frank","holocaust,inspirational,judaism" +"Teaching should be such that what is offered is perceived as a valuable gift and not as hard duty. Never regard study as duty but as the enviable opportunity to learn to know the liberating influence of beauty in the realm of the spirit for your own personal joy and to the profit of the community to which your later work belongs.","Albert Einstein","education" +"We'll meet at the theater tonight. I'll hold your seat 'til you get there. Once you get there; you're on your own.","Groucho Marx","humor" +"It is harder to crack prejudice than an atom.","Albert Einstein","humor,inspirational,science" +"I can accept anything, except what seems to be the easiest for most people: the half-way, the almost, the just-about, the in-between.","Ayn Rand","compromise,philosophy" +"A hospital bed is a parked taxi with the meter running.","Groucho Marx","funny" +"The best and most beautiful things in the world cannot be seen or even touched - they must be felt with the heart.","Helen Keller","best,beautiful,heart" +"No amount of experimentation can ever prove me right; a single experiment can prove me wrong.","Albert Einstein","science" +"I don't try to imagine a personal God; it suffices to stand in awe at the structure of the world, insofar as it allows our inadequate senses to appreciate it.","Albert Einstein","atheism,atheist,god,religion" +"Happiness is a state of activity.","Aristotle","activity,happiness" +"Although I am a typical loner in my daily life, my awareness of belonging to the invisible community of those who strive for truth, beauty, and justice has prevented me from feelings of isolation.","Albert Einstein","albert-einstein,einstein,inspirational,loner" +"We may have all come on different ships, but we're in the same boat now.","Martin Luther King Jr.","cultural,inspirational,spiritual" +"I believe that God is in me as the sun is in the colour and fragrance of a flower - the Light in my darkness, the Voice in my silence.","Helen Keller","inspirational" +"You see, wire telegraph is a kind of a very, very long cat. You pull his tail in New York and his head is meowing in Los Angeles. Do you understand this? And radio operates exactly the same way: you send signals here, they receive them there. The only difference is that there is no cat.","Albert Einstein","analogy,humor,science" +"Familiarity breeds contempt and children.","Mark Twain","humor" +"Learning is an ornament in prosperity, a refuge in adversity, and a provision in old age.","Aristotle","adversity,education,learning,old-age,prosperity" +"A lot of people have gone further than they thought they could because someone else thought they could","Zig Ziglar","inspirational" +"Life is a succesion of lessons which must be lived to be understood.","Helen Keller","lessons,life" +"There are no traffic jams on the extra mile.","Zig Ziglar","inspirational" +"You're gonna have to serve somebody; well, it may be the devil, or it may be the Lord, but you're gonna have to serve somebody...","Bob Dylan","christianity,faith,god" +"Small is the number of them that see with their own eyes and feel with their own hearts.","Albert Einstein","heart,inspirational" +"Act the way you'd like to be and soon you'll be the way you'd like to act.","Bob Dylan","action,change,improvement,misattributed-to-leonard-cohen,motivational,success" +"It may be true that the law cannot make a man love me, but it can stop him from lynching me, and I think that's pretty important.","Martin Luther King Jr.","law,life,lynchings" +"Happiness depends more on the inward disposition of mind than on outward circumstances.","Benjamin Franklin","inspirational" +"A big leather-bound volume makes an ideal razorstrap. A thing book is useful to stick under a table with a broken caster to steady it. A large, flat atlas can be used to cover a window with a broken pane. And a thick, old-fashioned heavy book with a clasp is the finest thing in the world to throw at a noisy cat.","Mark Twain","books,humor" +"No man's life, liberty, or property are safe while the legislature is in session.","Mark Twain","government,humor,politics" +"Don't say the old lady screamed. Bring her on and let her scream.","Mark Twain","on-writing,writing" +"Until justice rolls down like water and righteousness like a mighty stream.","Martin Luther King Jr.","america,freedom,human-rights,inspirational,justice,peace,pride,righteousness,stream,water" +"Four things to learn in life: To think clearly without hurry or confusion; To love everybody sincerely; To act in everything with the highest motives; To trust God unhesitatingly.","Helen Keller","inspirational" +"Strange is our situation here on Earth. Each of us comes for a short visit, not knowing why, yet sometimes seeming to divine a purpose. From the standpoint of daily life, however, there is one thing we do know: that man is here for the sake of other men - above all for those upon whose smiles and well-being our own happiness depends.","Albert Einstein","inspirational-quotes" +"Everything that is really great and inspiring is created by the individual who can labor in freedom.","Albert Einstein","mind" +"Courage is the price that life exacts for granting peace.","Amelia Earhart","bravery,courage,life,peace" +"If a man could have half of his wishes, he would double his troubles.","Benjamin Franklin","wisdom" +"In religion and politics people’s beliefs and convictions are in almost every case gotten at second-hand, and without examination, from authorities who have not themselves examined the questions at issue but have taken them at second-hand from other non-examiners, whose opinions about them were not worth a brass farthing.","Mark Twain","beliefs,politics,religion" +"What I’ve realized is that life doesn’t count for much unless you’re willing to do your small part to leave our children — all of our children — a better world. Any fool can have a child. That doesn’t make you a father. It’s the courage to raise a child that makes you a father.","Barack Obama","inspirational" +"A friend to all is a friend to none.","Aristotle","friend,none" +"It usually takes me two or three days to prepare an impromptu speech.","Mark Twain","humor,preparedness" +"I define nothing. Not beauty, not patriotism. I take each thing as it is, without prior rules about what it should be.","Bob Dylan","acceptance,bob,define,dylan,life" +"It does not matter how long you live, but how well you do it.","Martin Luther King Jr.","inspirational" +"It occurred to me by intuition, and music was the driving force behind that intuition. My discovery was the result of musical perception.","Albert Einstein","inspirational" +"You look at yourself and you accept yourself for who you are, and once you accept yourself for who you are you become a better person.","Oprah Winfrey","inspirational" +"Lots of people want to ride with you in the limo, but what you want is someone who will take the bus with you when the limo breaks down.","Oprah Winfrey","ride,bus,breaks" +"Who is wise? He that learns from everyone. Who is powerful? He that governs his passions. Who is rich? He that is content. Who is that? Nobody.","Benjamin Franklin","philosophy,wisdom" +"Paying alimony is like feeding hay to a dead horse.","Groucho Marx","divorce,funny" +"Occasionally in life there are those moments of unutterable fulfillment which cannot be completely explained by those symbols called words. Their meanings can only be articulated by the inaudible language of the heart.","Martin Luther King Jr.","ecstasy,euphoria,fulfillment,life,power-of-words" +"Learning without thought is labor lost; thought without learning is perilous.","Confucius","education,learning,thinking" +"I believe that Gandhi’s views were the most enlightened of all the political men in our time. We should strive to do things in his spirit: not to use violence in fighting for our cause, but by non-participation in anything you believe is evil.","Albert Einstein","causes,corporations,gandhi,truth,violence" +"The game of basketball has been everything to me. My place of refuge, place I've always gone where I needed comfort and peace. It's been the site of intense pain and the most intense feelings of joy and satisfaction. It's a relationship that has evolved over time, given me the greatest respect and love for the game.","Michael Jordan","sports,time,respect" +"I hate girls that giggle all the time... You hate any girl that David looks at.","Audrey Hepburn","humor" +"I believe in pink. I believe that laughing is the best calorie burner. I believe in kissing, kissing a lot. I believe in being strong when everything seems to be going wrong. I believe that happy girls are the prettiest girls. I believe that tomorrow is another day and I believe in miracles.","Audrey Hepburn","best,kissing,happy" +"You say I started out with practically nothing, but that isn't correct. We all start with all there is, it's how we use it that makes things possible.","Henry Ford","inspiration" +"Anyone who doesn't take truth seriously in small matters cannot be trusted in large ones either.","Albert Einstein","albert-einstein,truth" +"I wasn't saying whatever they're saying I was saying. I'm sorry I said it really. I never meant it to be a lousy anti-religious thing. I apologize if that will make you happy. I still don't know quite what I've done. I've tried to tell you what I did do but if you want me to apologize, if that will make you happy, then OK, I'm sorry.","John Lennon","apology,beatles,humor,humour,jesus,religion" +"Success is the doing, not the getting; in the trying, not the triumph. Success is a personal standard, reaching for the highest that is in us, becoming all that we can be. If we do our best, we are a success.","Zig Ziglar","doing,success" +"I have a great respect for incremental improvement, and I've done that sort of thing in my life, but I've always been attracted to the more revolutionary changes. I don't know why. Because they're harder. They're much more stressful emotionally. And you usually go through a period where everybody tells you that you've completely failed.","Steve Jobs","respect,great,changes" +"A bank is a place where they lend you an umbrella in fair weather and ask for it back when it begins to rain.","Robert Frost","humor,poetry" +"A society's competitive advantage will come not from how well its schools teach the multiplication and periodic tables, but from how well they stimulate imagination and creativity.","Albert Einstein","science" +"There are only a few notes. Just variations on a theme.","John Lennon","life,music" +"A man is accepted into a church for what he believes and he is turned out for what he knows.","Mark Twain","religion" +"It is this belief in a power larger than myself and other than myself which allows me to venture into the unknown and even the unknowable.","Maya Angelou","faith,inspirational" +"A man is never more truthful than when he acknowledges himself a liar.","Mark Twain","truth" +"What a wee little part of a person's life are his acts and his words! His real life is led in his head, and is known to none but himself.","Mark Twain","words,head,himself" +"Your success and happiness lie in you.","Helen Keller","happiness,inspirational-quotes,success" +"Ah, when to the heart of man Was it ever less than a treason To go with the drift of things, To yield with a grace to reason, And bow and accept the end Of a love or a season?","Robert Frost","love,poetry" +"Never allow the fear of striking out keep you from playing the game!","Babe Ruth","babe-ruth,baseball,cinderella-story,inspiration,motivation,quote,success" +"The whole secret of a successful life is to find out what is one's destiny to do, and then do it.","Henry Ford","success" +"The future doesn't belong to the light-hearted. It belongs to the brave.","Ronald Reagan","bravery,inspirational,reagan" +"An ounce of prevention is worth a pound of cure.","Benjamin Franklin","preparation,prevention,proverb,wisdom" +"Wishing to be friends is quick work, but friendship is a slow ripening fruit.","Aristotle","work,fruit,slow" +"One of the strongest motives that lead men to art and science is escape from everyday life with its painful crudity and hopeless dreariness, from the fetters of one's own ever-shifting desires. A finely tempered nature longs to escape from the personal life into the world of objective perception and thought.","Albert Einstein","art,life,science" +"Making a decision to write was a lot like deciding to jump into a frozen lake.","Maya Angelou","writing" +"America is too great for small dreams.","Ronald Reagan","inspirational" +"What I try to do is write. I may write for two weeks ‘the cat sat on the mat, that is that, not a rat,’.... And it might be just the most boring and awful stuff. But I try. When I’m writing, I write. And then it’s as if the muse is convinced that I’m serious and says, ‘Okay. Okay. I’ll come.","Maya Angelou","writing" +"And the only way to do great work is to love what you do.... Don't settle","Steve Jobs","inspirational" +"The Master said, “A true teacher is one who, keeping the past alive, is also able to understand the present.”(Analects 2.11)","Confucius","history,teachers,wisdom" +"I do not want the peace which passeth understanding, I want the understanding which bringeth peace.","Helen Keller","education,ispirational,learning,understanding" +"If books are not good company, where shall I find it?","Mark Twain","books" +"Remembering that you are going to die is the best way I know to avoid the trap of thinking you have something to lose.","Steve Jobs","death,hope,inspirational,life,motivatonal" +"I failed to communicate, that's why I chose to leave","Bob Dylan","life" +"What affects one in a major way, affects all in a minor way.","Martin Luther King Jr.","inspirational" +"Poetry is a way of taking life by the throat.","Robert Frost","life,poetry" +"…Christianity will go. It will vanish and shrink. I don't know what will go first, rock 'n' roll or Christianity. We're more popular than Jesus now. Jesus was all right, but his disciples were thick and ordinary. It's them twisting it that ruins it for me.","John Lennon","beatles,christianity,popular-culture,religion" +"Great spirits have always encountered opposition from mediocre minds. The mediocre mind is incapable of understanding the man who refuses to bow blindly to conventional prejudices and chooses instead to express his opinions courageously and honestly.","Albert Einstein","inspirational,opposition,wisdom" +"You better start swimming, or you'll sink like a stone. Because the Time's they are a-changing.","Bob Dylan","life,music" +"Mr. Right is coming, but he's in Africa and he's walking.","Oprah Winfrey","funny" +"That's what you want to do? Then nothing beats a trial but a failure. Give it everything you've got. I've told you many times, 'Cant do is like Dont Care.' Neither of them have a home.","Maya Angelou","inspirational" +"We are faced with the fact, my friends, that tomorrow is today. Procrastination is still the thief of time. Over the bleached bones and jumbled residues of numerous civilizations are written the pathetic words ‘Too Late’.","Martin Luther King Jr.","inspirational,political" +"I want all my senses engaged. Let me absorb the world's variety and uniqueness.","Maya Angelou","inspirational" +"Education: that which reveals to the wise, and conceals from the stupid, the vast limits of their knowledge.","Mark Twain","education" +"It is more shameful to distrust our friends than to be deceived by them.","Confucius","deceived,distrust,shameful" +"Don't be distracted by criticism. Remember ~ the only taste of success some people have is when they take a bite out of you.","Zig Ziglar","believe,inspiration,motivational,zig-ziglar" +"Freedom means the supremacy of human rights everywhere. Our support goes to those who struggle to gain those rights and keep them. Our strength is our unity of purpose. To that high concept there can be no end save victory.","Franklin D. Roosevelt","human-rights,truth" +"Poetry is finer and more philosophical than history; for poetry expresses the universal, and history only the particular.","Aristotle","poetry" +"No lake so still but it has its wave.No circle so perfect but that it has its blur.I would change things for you if I could; As I can't you must take them as they are.","Confucius","inspirational,life,philosophy" +"Like anybody, I would like to have a long life. Longevity has its place. But I'm not concerned about that now. I just want to do God's will.","Martin Luther King Jr.","foreboding,life" +"Rich People plan for three generationsPoor people plan for Saturday night","Gloria Steinem","class-distinction,inspirational" +"Never contract friendship with a man that is not better than thyself.","Confucius","contract,thyself" +"Each morning when I open my eyes I say to myself: I, not events, have the power to make me happy or unhappy today.","Groucho Marx","happiness,inspirational" +"The significant problems we have cannot be solved at the same level of thinking with which we created them.","Albert Einstein","inspirational,problems,science" +"I am overwhelmed by the grace and persistence of my people.","Maya Angelou","family,inspirational" +"To plant a garden is to believe in tomorrow.","Audrey Hepburn","hope,inspirational" +"youth is easily deceived because it is quick to hope.","Aristotle","deceit,gullibility,hope,wisdom,youth" +"As our circle of knowledge expands, so does the circumference of darkness surrounding it.","Albert Einstein","knowledge" +"It doesn't matter who you are, where you come from. The ability to triumph begins with you - always.","Oprah Winfrey","inspirational" +"Knowledge is merely brilliance in organization of ideas and not wisdom. The truly wise person goes beyond knowledge.","Confucius","wisdom" +"In the view of such harmony in the cosmos which I, with my limited human mind, am able to recognize, there are yet people who says there is no God. But what makes me really angry is that they quote me for support of such views. (The Expanded Quotable Einstein, Princeton University, page 214)","Albert Einstein","creation,god" +"The Truth is found when men (and Women) are free to pursue it.","Franklin D. Roosevelt","freedom,liberty,truth" +"Don't be trapped by dogma — which is living with the results of other people's thinking.","Steve Jobs","life,thinking" +"When You Cease To Exist, Then Who Will You Blame?","Bob Dylan","inspirational" +"A man's ethical behaviour should be based effectually on sympathy, education, and social ties and needs; no religious basis is necessary. Man would indeed be in a poor way if he had to be restrained by fear of punishment and hope of reward after death.","Albert Einstein","education,ethics,morality,needs,religion,sociality,sympathy" +"Segregation shaped me; education liberated me.","Maya Angelou","education,inspirational,learning,race" +"The gods too are fond of a joke.","Aristotle","god,humor,jokes,religion" +"A war of ideas can no more be won without books than a naval war can be won without ships. Books, like ships, have the toughest armor, the longest cruising range, and mount the most powerful guns.","Franklin D. Roosevelt","boats,books,ideas,sailing,ships" +"Take one cup of love, two cups of loyalty, three cups of forgiveness, four quarts of faith and one barrel of laughter. Take love and loyalty and mix them thoroughly with faith; blend with tenderness, kindness and understanding. Add friendship and hope. Sprinkle abundantly with laughter. Bake it with sunshine. Wrap it regularly with lots of hugs. Serve generous helpings daily.","Zig Ziglar","happiness,marriage" +"If I could give you information of my life it would be to show how a woman of very ordinary ability has been led by God in strange and unaccustomed paths to do in His service what He has done in her. And if I could tell you all, you would see how God has done all, and I nothing. I have worked hard, very hard, that is all; and I have never refused God anything.","Florence Nightingale","god,service,work" +"Life is about becoming more of who you really are....","Oprah Winfrey","inspirational" +"If God would have wanted us to live in a permissive society He would have given us Ten Suggestions and not Ten Commandments.","Zig Ziglar","religion" +"Go as far as you can see and you will see further.","Zig Ziglar","inspirational,motivation,success" +"Without continual growth and progress, such words as improvement, achievement, and success have no meaning.","Benjamin Franklin","growth,progress,words" +"To avoid being mistaken for a sellout, I chose my friends carefully. The more politically active black students. The foreign students. The Chicanos. The Marxist professors and structural feminists and punk-rock performance poets.","Barack Obama","black,students,active" +"Anyone who fights for the future, lives in it today.","Ayn Rand","idealism,philosophy" +"My philosophy, in essence, is the concept of man as a heroic being, with his own happiness as the moral purpose of his life, with productive achievement as his noblest activity, and reason as his only absolute.","Ayn Rand","philosophy" +"A tyrant must put on the appearance of uncommon devotion to religion. Subjects are less apprehensive of illegal treatment from a ruler whom they consider god-fearing and pious. On the other hand, they do less easily move against him, believing that he has the gods on his side.","Aristotle","morality,politics,religion" +"Curiosity is more important than knowledge.","Albert Einstein","curiosity,knowledge" +"I said to my children, 'I'm going to work and do everything that I can do to see that you get a good education. I don't ever want you to forget that there are millions of God's children who will not and cannot get a good education, and I don't want you feeling that you are better than they are. For you will never be what you ought to be until they are what they ought to be.","Martin Luther King Jr.","altruism,education,potential,religion" +"God made a beauteous garden With lovely flowers strown,But one straight, narrow pathway That was not overgrown.And to this beauteous garden He brought mankind to live,And said "To you, my children, These lovely flowers I give.Prune ye my vines and fig trees, With care my flowers tend,But keep the pathway open Your home is at the end."God's Garden","Robert Frost","frost,gardens,god" +"Herodotus says, "Very few things happen at the right time, and the rest do not happen at all: the conscientious historian will correct these defects.","Mark Twain","creativity,historians,history,truth" +"You will find the key to success under the alarm clock.","Benjamin Franklin","success" +"Character cannot be developed in ease and quiet. Only through experience of trial and suffering can the soul be strengthened, ambition inspired, and success achieved.","Helen Keller","strength,experience" +"The happiness you feel is in direct proportion to the love you give.","Oprah Winfrey","happiness,life,love" +"If one tries to navigate unknown waters one runs the risk of shipwreck","Albert Einstein","wisdom" +"You teach people how to treat you.","Oprah Winfrey","inspirational,oprah" +"Nothing exists but you. And you are but a thought.","Mark Twain","atheism,inspirational" +"It's sad if people think that's (homemaking) a dull existance, [but] you can't just buy an apartment and furnish it and walk away. It's the flowers you choose, the music you play, the smile you have waiting. I want it to be gay and cheerful, a haven in this troubled world. I don't want my husband and children to come home and find a rattled woman. Our era is already rattled enough, isn't it?","Audrey Hepburn","homemaking,inspirational,motherhood" +"They say that patriotism is the last refuge to which a scoundrel clings steal a little and they throw you in jail. Steal a lot and then they make you king.","Bob Dylan","patriotism,scoundrels,truth,wisdom" +"The test of any good fiction is that you should care something for the characters; the good to succeed, the bad to fail. The trouble with most fiction is that you want them all to land in hell together, as quickly as possible.","Mark Twain","writing" +"Wit is the sudden marriage of ideas which before their union were not perceived to have any relation.","Mark Twain","cleverness,writing" +"Learn as if you were not reaching your goal and as though you were scared of missing it","Confucius","confucious,inspirational,learning,life" +"Walking with a friend in the dark is better than walking alone in the light.","Helen Keller","alone,dark,light" +"For dear me, why abandon a beliefMerely because it ceases to be true","Robert Frost","faith,truth" +"The way to wealth is as plain as the way to market. It depends chiefly on two words, industry and frugality: that is, waste neither time nor money, but make the best use of both. Without industry and frugality nothing will do, and with them everything.","Benjamin Franklin","advice-for-daily-living,enterprise,frugality,success" +"Without feelings of respect, what is there to distinguish men from beasts?","Confucius","philosophical,understanding,wisdom" +"One should never use exclamation points in writing. It is like laughing at your own joke.","Mark Twain","advice,clever,exclamation-point,writing" +"Combinatory play seems to be the essential feature in productive thought.","Albert Einstein","education,learning,play" +"Every form of happiness if one, every desire is driven by the same motor--by our love for a single value, for the highest potentiality of our own existence--and every achievement is an expression of it.","Ayn Rand","galt,objectivism,philosophy,taggart" +"Economic power is exercised by means of a positive, by offering men a reward, an incentive, a payment, a value; political power is exercised by means of a negative, by the threat of punishment, injury, imprisonment, destruction. The businessman's tool is values; the bureaucrat's tool is fear.","Ayn Rand","businessmen,capitalism,force,government,money,philosophy,profit" +"That which is impenetrable to us really exists. Behind the secrets of nature remains something subtle, intangible, and inexplicable. Veneration for this force beyond anything that we can comprehend is my religion.","Albert Einstein","religion" +"he who will not economize will have to agonize","Confucius","frugality,ignorance,knowledge,suffering,wisdom" +"Art is not the application of a canon of beauty but what the instinct and the brain can conceive beyond any canon. When we love a woman we don't start measuring her limbs.","Pablo Picasso","love,beauty,woman" +"The best way to see Faith is to shut the eye of Reason.","Benjamin Franklin","wisdom" +"To do more for the world than the world does for you - that is success.","Henry Ford","success" +"It makes no difference where you go, there you are. And it makes no difference what you have, there’s always more to want. Until you are happy with who you are, you will never be happy because of what you have.","Zig Ziglar","grateful,happiness" +"The right to search for the truth implies also a duty; one must not conceal any part of what one has recognized to be the truth.","Albert Einstein","truth" +"When you have faults, do not fear to abandon them.","Confucius","advice,life,success" +"I find relief from the questions only when I concede that I am not obliged to know everything. I remind myself it is sufficient to know what I know, and that what I know, may not always be true.","Maya Angelou","death,letter-to-my-daughter" +"I confess to wincing every so often at a poorly chosen word, a mangled sentence, an expression of emotion that seems indulgent or overly practiced. I have the urge to cut the book by fifty pages or so, possessed as I am with a keener appreciation for brevity.","Barack Obama","writing" +"He who speaks without modesty will find it difficult to make his words good.","Confucius","inspirational-quotes" +"An attempt to achieve the good by force is like an attempt to provide a man with a picture gallery at the price of cutting out his eyes.","Ayn Rand","force,good,philosophy" +"Listen. Pay attention. Treasure every moment.","Oprah Winfrey","wisdom" +"It is a very grave mistake to think that the enjoyment of seeing and searching can be promoted by means of coercion and a sense of duty. To the contrary, I believe it would be possible to rob even a healthy beast of prey of its voraciousness, if it were possible, with the aid of a whip, to force the beast to devour continuously, even when not hungry.","Albert Einstein","education,learning" +"Deux choses sont infinies : l’Univers et la bêtise humaine. Mais, en ce qui concerne l’Univers, je n’en ai pas encore acquis la certitude absolue.","Albert Einstein","bêtise-humaine,einstein,french,humour,infini,philosophie,science,universe" +"La vie est une aventure audacieuse ou alors elle n'est rien.","Helen Keller","aventure,inspiration,vie" +"All pain is the same.","Oprah Winfrey","pain,wisdom" +"I couldn't tell fact from fiction,Or if the dream was trueMy only sure predictionIn this world was you.I'd touch your features inchly. Beard love and dared the cost, The sented spiel reeled me unreal And I found my senses lost.","Maya Angelou","inspiration,poetry" +"It is this mythical, or rather symbolic, content of the religious traditions which is likely to come into conflict with science. This occurs whenever this religious stock of ideas contains dogmatically fixed statements on subjects which belong in the domain of science.","Albert Einstein","religion,science" +"Try and penetrate with our limited means the secrets of nature and you will find that, behind all the descernible laws and connections, there remains something subtle, intangible and inexplicable. Veneration for this force beyond anything that we can comprehend is my religion. To that extent I am, in fact, religious.","Albert Einstein","science,spirituality" +"We have not the reverent feeling for the rainbow that the savage has, because we know how it is made. We have lost as much as we gained by prying into that matter.","Mark Twain","awe,science" +"If you're silent for a long time, people just arrive in your mind.","Alice Walker","characters,mind,people,silence,silent,writers,writing" +"I speak to the Black experience, but I am always talking about the human condition--about what we can endure, dream, fail at and survive.","Maya Angelou","writing" +"My best friend is the one who brings out the best in me.","Henry Ford","best,friend,brings" +"An inventor is a man who asks 'Why?' of the universe and lets nothing stand between the answer and his mind.","Ayn Rand","philosophy" +"But yield who will to their separation, My object in living is to uniteMy avocation and my vocationAs my two eyes make one in sight.","Robert Frost","goals,inspiration" +"A moment or an eternity—did it matter? Life, undefeated, existed and could exist.","Ayn Rand","life,philosophy" +"Faith is taking the first step even when you don't see the whole staircase.","Martin Luther King Jr.","faith" +"Oh juventud nunca dejes de pensar...","Albert Einstein","science" +"To study and constantly, is this not a pleasure? To have friends come from far away places, is this not a joy? If people do not recognize your worth, but this does not worry you, are you not a true gentleman?","Confucius","good-things,inspiration,life" +"In any compromise between food and poison, it is only death that can win. In any compromise between good and evil, it is only evil that can profit.","Ayn Rand","evil,good,philosophy" +"One more thing...","Steve Jobs","inspiration" +"Man is here for the sake of other men - above all for those upon whose smiles and well-being our own happiness depends.","Albert Einstein","happiness,purpose" +"And now I see the face of god, and I raise this god over the earth, this god whom men have sought since men came into being, this god who will grant them joy and peace and pride. This god, this one word: 'I.","Ayn Rand","individualism,philosophy" +"Definitions are the guardians of rationality, the first line of defense against the chaos of mental disintegration.","Ayn Rand","epistemology,philosophy,reason" +"The spread of evil is the symptom of a vacuum. Whenever evil wins, it is only by default: by the moral failure of those who evade the fact that there can be no compromise on basic principles.","Ayn Rand","evil,philosophy" +"A culture is made — or destroyed — by its articulate voices.","Ayn Rand","philosophy" +"The doorstep to the temple of wisdom is a knowledge of our own ignorance.","Benjamin Franklin","knowledge" +"First of all, let me assert my firm belief that the only thing we have to fear is fear itself - nameless, unreasoning, unjustified terror which paralyzes needed efforts to convert retreat into advance.","Franklin D. Roosevelt","fear,inspiration" +"Aristotle may be regarded as the cultural barometer of Western history. Whenever his influence dominated the scene, it paved the way for one of history's brilliant eras; whenever it fell, so did mankind.","Ayn Rand","aristotle,philosophy,reason" +"It is the kindness to take in a stranger when the levees break; the selflessness of workers who would rather cut their hours than see a friend lose their job which sees us through our darkest hours. It is the firefighter's courage to storm a stairway filled with smoke, but also a parent's willingness to nurture a child, that finally decides our fate.","Barack Obama","america,inauguration,inspiration" +"One today is worth two tomorrows.","Benjamin Franklin","today,worth,tomorrows" +"Your success and happiness lies in you. Resolve to keep happy, and your joy and you shall form an invicible host against difficulties.","Helen Keller","happiness,helen-keller,inspirational-life,life,success" +"But I don't know how to fight. All I know how to do is stay alive.","Alice Walker","faith,life,sad,western" +"I have an unshaken conviction that democracy can never be undermined if we maintain our library resources and a national intelligence capable of utilizing them."[Letter to Herbert Putnam; in: Waters, Edward N.: Herbert Putnam: the tallest little man in the world; Quarterly Journal of the Library of Congress 33:2 (April 1976), p. 171]","Franklin D. Roosevelt","democracy,education,libraries" +"Some people get an education without going to college. The rest get it after they get out.","Mark Twain","college,education" +"Whatever their future, at the dawn of their lives, men seek a noble vision of man's nature and of life's potential.","Ayn Rand","idealism,philosophy" +"Contrary to the ecologists, nature does not stand still and does not maintain the kind of equilibrium that guarantees the survival of any particular species - least of all the survival of her greatest and most fragile product: man.","Ayn Rand","environmentalism,man,philosophy" +"Games lubricate the body and mind.","Benjamin Franklin","body,games,invigorate,lubricate,mind,refresh,renew,replenish" +"In scientific thinking are always present elements of poetry. Science and music requires a thought homogeneous.","Albert Einstein","music,poetry,science" +"I would not think that philosophy and reason themselves will be man's guide in the foreseeable future; however, they will remain the most beautiful sanctuary they have always been for the select few.","Albert Einstein","philosophy,thought" +"Man is an end in himself.","Ayn Rand","individualism,life,objectivism,philosophy,success" +"If a man has not discovered something that he will die for, he isn't fit to live","Martin Luther King Jr.","inspirational-quotes" +"Fear is the Fatal killer of Desire.","Zig Ziglar","fear,inspiration,iuindia-com,marketing,motivational,rohitdaga" +"Nor is there wanting in the pressSome spirit to stand simply forth,Heroic in it nakedness,Against the uttermost of earth.The tale of earth's unhonored thingsSounds nobler there than 'neath the sun;And the mind whirls and the heart sings,And a shout greets the daring one.","Robert Frost","life-quotes,poetry,poets,quotes,robert-frost" +"Joy is the holy fire that keeps our purpose warm and our intelligence aglow.","Helen Keller","happiness,joy,purpose" +"Try not to become a person of success, but rather try to become a person of value.","Albert Einstein","success" +"People often say that motivation doesn't last. Well, neither does bathing - that's why we recommend it daily.","Zig Ziglar","daily,last,bathing" +"Find a beautiful piece of art. If you fall in love with Van Gogh or Matisse or John Oliver Killens, or if you fall love with the music of Coltrane, the music of Aretha Franklin, or the music of Chopin - find some beautiful art and admire it, and realize that that was created by human beings just like you, no more human, no less.","Maya Angelou","love,music,beautiful" +"The female is, as it were, a mutilated male, and the catamenia are semen, only not pure; for there is only one thing they have not in them, the principle of soul.","Aristotle","female,male,mutilated,soul" +"When I lose the sense of motivation and the sense to prove something as a basketball player, it's time for me to move away from the game.","Michael Jordan","time,basketball,game" +"The monotony and solitude of a quiet life stimulates the creative mind.","Albert Einstein","creative,solitude,quiet" +"Wenn du weißt, behaupte, dass du es weißt. Und wenn du etwas nicht weißt, gib zu, dass du es nicht weißt. Das ist Wissen.","Confucius","knowledge,wisdom" +"Let it be told to the future world that in the depth of winter, when nothing but hope and virtue could survive, that the city and the country, alarmed at one common danger, came forth to meet it.","Barack Obama","america,inauguration,inspiration" +"We will restore science to its rightful place and wield technology's wonders to raise health care's quality and lower its cost.","Barack Obama","inspirational,politics,science" +"Phantasie ist wichtiger als Wissen, denn Wissen ist begrenzt.","Albert Einstein","inspiration" +"If that which we have found is the corruption of solitude, then what can men wish for save corruption? If this is the great evil of being alone, than what is good and what is evil?","Ayn Rand","dystopian,philosophy" +"‎Teachers matter. So instead of bashing them, or defending the status quo, let’s offer schools a deal. Give them the resources to keep good teachers on the job, and reward the best ones. In return, grant schools flexibility: To teach with creativity and passion; to stop teaching to the test; and to replace teachers who just aren’t helping kids learn.","Barack Obama","education" +"If you accept the expectations of others, especially negative ones, then you never will change the outcome.","Michael Jordan","change,negative,accept" diff --git a/examples/quotes/backend/quotes.py b/examples/quotes/backend/quotes.py new file mode 100644 index 000000000..4492d5e7e --- /dev/null +++ b/examples/quotes/backend/quotes.py @@ -0,0 +1,179 @@ +import asyncio +import csv +import os +from time import time +from typing import Annotated + +from fastapi import FastAPI, HTTPException +from pydantic import BaseModel, Field, ValidationError +from sentence_transformers import SentenceTransformer + +from elasticsearch import NotFoundError +from elasticsearch.dsl.pydantic import AsyncBaseESModel +from elasticsearch import dsl + +model = SentenceTransformer("all-MiniLM-L6-v2") +dsl.async_connections.create_connection(hosts=[os.environ['ELASTICSEARCH_URL']]) + + +class Quote(AsyncBaseESModel): + quote: str + author: Annotated[str, dsl.Keyword()] + tags: Annotated[list[str], dsl.Keyword()] + embedding: Annotated[list[float], dsl.DenseVector()] = Field(init=False, default=[]) + + class Index: + name = 'quotes' + + +class Tag(BaseModel): + tag: str + count: int + + +class SearchRequest(BaseModel): + query: str + filters: list[str] + knn: bool + start: int + + +class SearchResponse(BaseModel): + quotes: list[Quote] + tags: list[Tag] + start: int + total: int + + +app = FastAPI( + title="Quotes API", + version="1.0.0", +) + + +@app.get("/api/quotes/{id}") +async def get_quote(id: str) -> Quote: + doc = None + try: + doc = await Quote._doc.get(id) + except NotFoundError: + pass + if not doc: + raise HTTPException(status_code=404, detail="Item not found") + return Quote.from_doc(doc) + + +@app.post("/api/quotes", status_code=201) +async def create_quote(req: Quote) -> Quote: + embed_quotes([req]) + doc = req.to_doc() + doc.meta.id = "" + await doc.save(refresh=True) + return Quote.from_doc(doc) + + +@app.put("/api/quotes/{id}") +async def update_quote(id: str, quote: Quote) -> Quote: + doc = None + try: + doc = await Quote._doc.get(id) + except NotFoundError: + pass + if not doc: + raise HTTPException(status_code=404, detail="Item not found") + if quote.quote: + embed_quotes([quote]) + doc.quote = quote.quote + doc.embedding = quote.embedding + if quote.author: + doc.author = quote.author + if quote.tags: + doc.tags = quote.tags + await doc.save(refresh=True) + return Quote.from_doc(doc) + + +@app.delete("/api/quotes/{id}", status_code=204) +async def delete_quote(id: str) -> None: + doc = None + try: + doc = await Quote._doc.get(id) + except NotFoundError: + pass + if not doc: + raise HTTPException(status_code=404, detail="Item not found") + await doc.delete(refresh=True) + + +@app.post('/api/search') +async def search_quotes(req: SearchRequest) -> SearchResponse: + s = Quote._doc.search() + if req.query == '': + s = s.query(dsl.query.MatchAll()) + elif req.knn: + query_vector = model.encode(req.query).tolist() + s = s.query(dsl.query.Knn(field=Quote._doc.embedding, query_vector=query_vector)) + else: + s = s.query(dsl.query.Match(quote=req.query)) + for tag in req.filters: + s = s.filter(dsl.query.Terms(tags=[tag])) + s.aggs.bucket('tags', dsl.aggs.Terms(field=Quote._doc.tags, size=100)) + + r = await s[req.start:req.start + 25].execute() + tags = [(tag.key, tag.doc_count) for tag in r.aggs.tags.buckets] + quotes = [Quote.from_doc(hit) for hit in r.hits] + total = r['hits'].total.value + + return SearchResponse( + quotes=quotes, + tags=[Tag(tag=t[0], count=t[1]) for t in tags], + start=req.start, + total=total + ) + + +def embed_quotes(quotes): + embeddings = model.encode([q.quote for q in quotes]) + for q, e in zip(quotes, embeddings): + q.embedding = e.tolist() + + +async def ingest_quotes(): + if await Quote._doc._index.exists(): + await Quote._doc._index.delete() + await Quote._doc.init() + + def ingest_progress(count, start): + elapsed = time() - start + print(f'\rIngested {count} quotes. ({count / elapsed:.0f}/sec)', end='') + + async def get_next_quote(): + quotes: list[Quote] = [] + with open('quotes.csv') as f: + reader = csv.DictReader(f) + count = 0 + start = time() + for row in reader: + q = Quote(quote=row['quote'], author=row['author'], + tags=row['tags'].split(',')) + quotes.append(q) + if len(quotes) == 512: + embed_quotes(quotes) + for q in quotes: + yield q.to_doc() + count += len(quotes) + ingest_progress(count, start) + quotes = [] + if len(quotes) > 0: + embed_quotes(quotes) + for q in quotes: + yield q.to_doc() + count += len(quotes) + ingest_progress(count, start) + + await Quote._doc.bulk(get_next_quote()) + print("\nIngest complete.") + + +if __name__ == "__main__": + asyncio.run(ingest_quotes()) diff --git a/examples/quotes/backend/requirements.txt b/examples/quotes/backend/requirements.txt new file mode 100644 index 000000000..40862ea54 --- /dev/null +++ b/examples/quotes/backend/requirements.txt @@ -0,0 +1,163 @@ +# +# This file is autogenerated by pip-compile with Python 3.12 +# by the following command: +# +# pip-compile pyproject.toml +# +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.12.15 + # via elasticsearch +aiosignal==1.4.0 + # via aiohttp +annotated-types==0.7.0 + # via pydantic +anyio==4.11.0 + # via starlette +attrs==25.3.0 + # via aiohttp +certifi==2025.8.3 + # via + # elastic-transport + # requests +charset-normalizer==3.4.3 + # via requests +click==8.3.0 + # via uvicorn +elastic-transport==8.17.1 + # via elasticsearch +elasticsearch[async]==8.19.1 + # via quotes (pyproject.toml) +fastapi==0.117.1 + # via quotes (pyproject.toml) +filelock==3.19.1 + # via + # huggingface-hub + # torch + # transformers +frozenlist==1.7.0 + # via + # aiohttp + # aiosignal +fsspec==2025.9.0 + # via + # huggingface-hub + # torch +h11==0.16.0 + # via uvicorn +hf-xet==1.1.10 + # via huggingface-hub +huggingface-hub==0.35.0 + # via + # sentence-transformers + # tokenizers + # transformers +idna==3.10 + # via + # anyio + # requests + # yarl +jinja2==3.1.6 + # via torch +joblib==1.5.2 + # via scikit-learn +markupsafe==3.0.2 + # via jinja2 +mpmath==1.3.0 + # via sympy +multidict==6.6.4 + # via + # aiohttp + # yarl +networkx==3.5 + # via torch +numpy==2.3.3 + # via + # scikit-learn + # scipy + # transformers +orjson==3.11.3 + # via quotes (pyproject.toml) +packaging==25.0 + # via + # huggingface-hub + # transformers +pillow==11.3.0 + # via sentence-transformers +propcache==0.3.2 + # via + # aiohttp + # yarl +pydantic==2.11.9 + # via fastapi +pydantic-core==2.33.2 + # via pydantic +python-dateutil==2.9.0.post0 + # via elasticsearch +pyyaml==6.0.2 + # via + # huggingface-hub + # transformers +regex==2025.9.18 + # via transformers +requests==2.32.5 + # via + # huggingface-hub + # transformers +safetensors==0.6.2 + # via transformers +scikit-learn==1.7.2 + # via sentence-transformers +scipy==1.16.2 + # via + # scikit-learn + # sentence-transformers +sentence-transformers==5.1.1 + # via quotes (pyproject.toml) +six==1.17.0 + # via python-dateutil +sniffio==1.3.1 + # via anyio +starlette==0.48.0 + # via fastapi +sympy==1.14.0 + # via torch +threadpoolctl==3.6.0 + # via scikit-learn +tokenizers==0.22.1 + # via transformers +torch==2.8.0 + # via sentence-transformers +tqdm==4.67.1 + # via + # huggingface-hub + # sentence-transformers + # transformers +transformers==4.56.2 + # via sentence-transformers +typing-extensions==4.15.0 + # via + # aiosignal + # anyio + # elasticsearch + # fastapi + # huggingface-hub + # pydantic + # pydantic-core + # sentence-transformers + # starlette + # torch + # typing-inspection +typing-inspection==0.4.1 + # via pydantic +urllib3==2.5.0 + # via + # elastic-transport + # requests +uvicorn==0.36.0 + # via quotes (pyproject.toml) +yarl==1.20.1 + # via aiohttp + +# The following packages are considered to be unsafe in a requirements file: +# setuptools diff --git a/examples/quotes/eslint.config.js b/examples/quotes/eslint.config.js new file mode 100644 index 000000000..b19330b10 --- /dev/null +++ b/examples/quotes/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs['recommended-latest'], + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/examples/quotes/index.html b/examples/quotes/index.html new file mode 100644 index 000000000..283f6d8ef --- /dev/null +++ b/examples/quotes/index.html @@ -0,0 +1,13 @@ + + + + + + + Elasticsearch + Pydantic Demo + + +
+ + + diff --git a/examples/quotes/package-lock.json b/examples/quotes/package-lock.json new file mode 100644 index 000000000..9d7913b83 --- /dev/null +++ b/examples/quotes/package-lock.json @@ -0,0 +1,3756 @@ +{ + "name": "quotes", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "quotes", + "version": "0.0.0", + "hasInstallScript": true, + "dependencies": { + "boostrap": "^2.0.0", + "bootstrap": "^5.3.8", + "react": "^19.1.1", + "react-bootstrap": "^2.10.10", + "react-dom": "^19.1.1", + "react-router": "^7.9.2" + }, + "devDependencies": { + "@eslint/js": "^9.36.0", + "@types/react": "^19.1.13", + "@types/react-dom": "^19.1.9", + "@vitejs/plugin-react": "^5.0.3", + "eslint": "^9.36.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.4.0", + "typescript": "~5.8.3", + "typescript-eslint": "^8.44.0", + "vite": "^7.1.7" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", + "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz", + "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@restart/hooks": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", + "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.9.4.tgz", + "integrity": "sha512-N4C7haUc3vn4LTwVUPlkJN8Ach/+yIMvRuTVIhjilNHqegY60SGLrzud6errOMNJwSnmYFnt1J0H/k8FE3A4KA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@popperjs/core": "^2.11.8", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.5.0", + "@types/warning": "^3.0.3", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.4", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/@restart/hooks": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.5.1.tgz", + "integrity": "sha512-EMoH04NHS1pbn07iLTjIjgttuqb7qu4+/EyhAx27MHpoENcB2ZdSsLTNxmKD+WEPnZigo62Qc8zjGnNxoSE/5Q==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.14.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.35", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.35.tgz", + "integrity": "sha512-slYrCpoxJUqzFDDNlvrOYRazQUNRvWPjXA17dAOISY3rDMxX6k8K4cj2H+hEYMHF81HO3uNd5rHVigAWRM5dSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.2.tgz", + "integrity": "sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.2.tgz", + "integrity": "sha512-cqFSWO5tX2vhC9hJTK8WAiPIm4Q8q/cU8j2HQA0L3E1uXvBYbOZMhE2oFL8n2pKB5sOCHY6bBuHaRwG7TkfJyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.2.tgz", + "integrity": "sha512-vngduywkkv8Fkh3wIZf5nFPXzWsNsVu1kvtLETWxTFf/5opZmflgVSeLgdHR56RQh71xhPhWoOkEBvbehwTlVA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.2.tgz", + "integrity": "sha512-h11KikYrUCYTrDj6h939hhMNlqU2fo/X4NB0OZcys3fya49o1hmFaczAiJWVAFgrM1NCP6RrO7lQKeVYSKBPSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.2.tgz", + "integrity": "sha512-/eg4CI61ZUkLXxMHyVlmlGrSQZ34xqWlZNW43IAU4RmdzWEx0mQJ2mN/Cx4IHLVZFL6UBGAh+/GXhgvGb+nVxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.2.tgz", + "integrity": "sha512-QOWgFH5X9+p+S1NAfOqc0z8qEpJIoUHf7OWjNUGOeW18Mx22lAUOiA9b6r2/vpzLdfxi/f+VWsYjUOMCcYh0Ng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.2.tgz", + "integrity": "sha512-kDWSPafToDd8LcBYd1t5jw7bD5Ojcu12S3uT372e5HKPzQt532vW+rGFFOaiR0opxePyUkHrwz8iWYEyH1IIQA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.2.tgz", + "integrity": "sha512-gKm7Mk9wCv6/rkzwCiUC4KnevYhlf8ztBrDRT9g/u//1fZLapSRc+eDZj2Eu2wpJ+0RzUKgtNijnVIB4ZxyL+w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.2.tgz", + "integrity": "sha512-66lA8vnj5mB/rtDNwPgrrKUOtCLVQypkyDa2gMfOefXK6rcZAxKLO9Fy3GkW8VkPnENv9hBkNOFfGLf6rNKGUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.2.tgz", + "integrity": "sha512-s+OPucLNdJHvuZHuIz2WwncJ+SfWHFEmlC5nKMUgAelUeBUnlB4wt7rXWiyG4Zn07uY2Dd+SGyVa9oyLkVGOjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.2.tgz", + "integrity": "sha512-8wTRM3+gVMDLLDdaT6tKmOE3lJyRy9NpJUS/ZRWmLCmOPIJhVyXwjBo+XbrrwtV33Em1/eCTd5TuGJm4+DmYjw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.2.tgz", + "integrity": "sha512-6yqEfgJ1anIeuP2P/zhtfBlDpXUb80t8DpbYwXQ3bQd95JMvUaqiX+fKqYqUwZXqdJDd8xdilNtsHM2N0cFm6A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.2.tgz", + "integrity": "sha512-sshYUiYVSEI2B6dp4jMncwxbrUqRdNApF2c3bhtLAU0qA8Lrri0p0NauOsTWh3yCCCDyBOjESHMExonp7Nzc0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.2.tgz", + "integrity": "sha512-duBLgd+3pqC4MMwBrKkFxaZerUxZcYApQVC5SdbF5/e/589GwVvlRUnyqMFbM8iUSb1BaoX/3fRL7hB9m2Pj8Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.2.tgz", + "integrity": "sha512-tzhYJJidDUVGMgVyE+PmxENPHlvvqm1KILjjZhB8/xHYqAGeizh3GBGf9u6WdJpZrz1aCpIIHG0LgJgH9rVjHQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.2.tgz", + "integrity": "sha512-opH8GSUuVcCSSyHHcl5hELrmnk4waZoVpgn/4FDao9iyE4WpQhyWJ5ryl5M3ocp4qkRuHfyXnGqg8M9oKCEKRA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.2.tgz", + "integrity": "sha512-LSeBHnGli1pPKVJ79ZVJgeZWWZXkEe/5o8kcn23M8eMKCUANejchJbF/JqzM4RRjOJfNRhKJk8FuqL1GKjF5oQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.2.tgz", + "integrity": "sha512-uPj7MQ6/s+/GOpolavm6BPo+6CbhbKYyZHUDvZ/SmJM7pfDBgdGisFX3bY/CBDMg2ZO4utfhlApkSfZ92yXw7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.2.tgz", + "integrity": "sha512-Z9MUCrSgIaUeeHAiNkm3cQyst2UhzjPraR3gYYfOjAuZI7tcFRTOD+4cHLPoS/3qinchth+V56vtqz1Tv+6KPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.2.tgz", + "integrity": "sha512-+GnYBmpjldD3XQd+HMejo+0gJGwYIOfFeoBQv32xF/RUIvccUz20/V6Otdv+57NE70D5pa8W/jVGDoGq0oON4A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.2.tgz", + "integrity": "sha512-ApXFKluSB6kDQkAqZOKXBjiaqdF1BlKi+/eqnYe9Ee7U2K3pUDKsIyr8EYm/QDHTJIM+4X+lI0gJc3TTRhd+dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.2.tgz", + "integrity": "sha512-ARz+Bs8kY6FtitYM96PqPEVvPXqEZmPZsSkXvyX19YzDqkCaIlhCieLLMI5hxO9SRZ2XtCtm8wxhy0iJ2jxNfw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.1.13", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.13.tgz", + "integrity": "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==", + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.9", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz", + "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/warning": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", + "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.1.tgz", + "integrity": "sha512-molgphGqOBT7t4YKCSkbasmu1tb1MgrZ2szGzHbclF7PNmOkSTQVHy+2jXOSnxvR3+Xe1yySHFZoqMpz3TfQsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.44.1", + "@typescript-eslint/type-utils": "8.44.1", + "@typescript-eslint/utils": "8.44.1", + "@typescript-eslint/visitor-keys": "8.44.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.44.1", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.1.tgz", + "integrity": "sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.44.1", + "@typescript-eslint/types": "8.44.1", + "@typescript-eslint/typescript-estree": "8.44.1", + "@typescript-eslint/visitor-keys": "8.44.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.1.tgz", + "integrity": "sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.44.1", + "@typescript-eslint/types": "^8.44.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.1.tgz", + "integrity": "sha512-NdhWHgmynpSvyhchGLXh+w12OMT308Gm25JoRIyTZqEbApiBiQHD/8xgb6LqCWCFcxFtWwaVdFsLPQI3jvhywg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.44.1", + "@typescript-eslint/visitor-keys": "8.44.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.1.tgz", + "integrity": "sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.1.tgz", + "integrity": "sha512-KdEerZqHWXsRNKjF9NYswNISnFzXfXNDfPxoTh7tqohU/PRIbwTmsjGK6V9/RTYWau7NZvfo52lgVk+sJh0K3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.44.1", + "@typescript-eslint/typescript-estree": "8.44.1", + "@typescript-eslint/utils": "8.44.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.1.tgz", + "integrity": "sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.1.tgz", + "integrity": "sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.44.1", + "@typescript-eslint/tsconfig-utils": "8.44.1", + "@typescript-eslint/types": "8.44.1", + "@typescript-eslint/visitor-keys": "8.44.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.1.tgz", + "integrity": "sha512-DpX5Fp6edTlocMCwA+mHY8Mra+pPjRZ0TfHkXI8QFelIKcbADQz1LUPNtzOFUriBB2UYqw4Pi9+xV4w9ZczHFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.44.1", + "@typescript-eslint/types": "8.44.1", + "@typescript-eslint/typescript-estree": "8.44.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.1.tgz", + "integrity": "sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.44.1", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.3.tgz", + "integrity": "sha512-PFVHhosKkofGH0Yzrw1BipSedTH68BFF8ZWy1kfUpCtJcouXXY0+racG8sExw7hw0HoX36813ga5o3LTWZ4FUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.4", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.35", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.6.tgz", + "integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/boostrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boostrap/-/boostrap-2.0.0.tgz", + "integrity": "sha512-JEeFMOweKeGXEM9rt95eaVISOkluG9aKcl0jQCETOVH9jynCZxuBZe2oWgcWJpj5wqYWZl625SnW7OgHT2Ineg==", + "deprecated": "Package no longer supported. Contact support@npmjs.com for more info.", + "license": "ISC" + }, + "node_modules/bootstrap": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz", + "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT", + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001743", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", + "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.223", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.223.tgz", + "integrity": "sha512-qKm55ic6nbEmagFlTFczML33rF90aU+WtrJ9MdTCThrcvDNdUHN4p6QfVN78U06ZmguqXIyMPyYhw2TrbDUwPQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", + "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.36.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.21.tgz", + "integrity": "sha512-MWDWTtNC4voTcWDxXbdmBNe8b/TxfxRFUL6hXgKWJjN9c1AagYEmpiFWBWzDw+5H3SulWUe1pJKTnoSdmk88UA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "license": "MIT", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", + "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-bootstrap": { + "version": "2.10.10", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.10.tgz", + "integrity": "sha512-gMckKUqn8aK/vCnfwoBpBVFUGT9SVQxwsYrp9yDHt0arXMamxALerliKBxr1TPbntirK/HGrUAHYbAeQTa9GHQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.9.4", + "@types/prop-types": "^15.7.12", + "@types/react-transition-group": "^4.4.6", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-dom": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.1" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.2.tgz", + "integrity": "sha512-i2TPp4dgaqrOqiRGLZmqh2WXmbdFknUyiCRmSKs0hf6fWXkTKg5h56b+9F22NbGRAMxjYfqQnpi63egzD2SuZA==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.52.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.2.tgz", + "integrity": "sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.2", + "@rollup/rollup-android-arm64": "4.52.2", + "@rollup/rollup-darwin-arm64": "4.52.2", + "@rollup/rollup-darwin-x64": "4.52.2", + "@rollup/rollup-freebsd-arm64": "4.52.2", + "@rollup/rollup-freebsd-x64": "4.52.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.2", + "@rollup/rollup-linux-arm-musleabihf": "4.52.2", + "@rollup/rollup-linux-arm64-gnu": "4.52.2", + "@rollup/rollup-linux-arm64-musl": "4.52.2", + "@rollup/rollup-linux-loong64-gnu": "4.52.2", + "@rollup/rollup-linux-ppc64-gnu": "4.52.2", + "@rollup/rollup-linux-riscv64-gnu": "4.52.2", + "@rollup/rollup-linux-riscv64-musl": "4.52.2", + "@rollup/rollup-linux-s390x-gnu": "4.52.2", + "@rollup/rollup-linux-x64-gnu": "4.52.2", + "@rollup/rollup-linux-x64-musl": "4.52.2", + "@rollup/rollup-openharmony-arm64": "4.52.2", + "@rollup/rollup-win32-arm64-msvc": "4.52.2", + "@rollup/rollup-win32-ia32-msvc": "4.52.2", + "@rollup/rollup-win32-x64-gnu": "4.52.2", + "@rollup/rollup-win32-x64-msvc": "4.52.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.44.1.tgz", + "integrity": "sha512-0ws8uWGrUVTjEeN2OM4K1pLKHK/4NiNP/vz6ns+LjT/6sqpaYzIVFajZb1fj/IDwpsrrHb3Jy0Qm5u9CPcKaeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.44.1", + "@typescript-eslint/parser": "8.44.1", + "@typescript-eslint/typescript-estree": "8.44.1", + "@typescript-eslint/utils": "8.44.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz", + "integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/examples/quotes/package.json b/examples/quotes/package.json new file mode 100644 index 000000000..75bbec010 --- /dev/null +++ b/examples/quotes/package.json @@ -0,0 +1,36 @@ +{ + "name": "quotes", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview", + "postinstall": "cd backend && python -m venv .venv && .venv/bin/pip install -r requirements.txt", + "backend": "cd backend && .venv/bin/uvicorn --port 5000 --reload quotes:app", + "ingest": "cd backend && .venv/bin/python quotes.py" + }, + "dependencies": { + "boostrap": "^2.0.0", + "bootstrap": "^5.3.8", + "react": "^19.1.1", + "react-bootstrap": "^2.10.10", + "react-dom": "^19.1.1", + "react-router": "^7.9.2" + }, + "devDependencies": { + "@eslint/js": "^9.36.0", + "@types/react": "^19.1.13", + "@types/react-dom": "^19.1.9", + "@vitejs/plugin-react": "^5.0.3", + "eslint": "^9.36.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.4.0", + "typescript": "~5.8.3", + "typescript-eslint": "^8.44.0", + "vite": "^7.1.7" + } +} diff --git a/examples/quotes/public/.keep b/examples/quotes/public/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/examples/quotes/screenshot.png b/examples/quotes/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..7b546085cb7243cfc7438ceb2e79c61261ee323e GIT binary patch literal 229765 zcmdqIhd-Nd-#<(#YAZ^uShYvBY7<-SJxVLKDr&0{LMcj#)zT_K?Y*^DT1phHy-RBE z6)PcY4>iq*h)6w% zh_202kP+^X(t=lsh$yQ(G&D?fH8i+Q{C(U!yj+Qh9=?8?MQ&mKgAK8K%Ge z6227em%qXfIwali!NKP_c<_Dq^*l);lgvlg)I`LnRM~*~tjjA#Nr@>1O9|nP{4mnMkj~jRUhfto zN)4N+Z5PqM`!hmFlwy?B^pnU3LT7w7 zcpdrN%x0Ct{h0Tip1=N$QyBCp75%D^{)g>nFyI^$+Iii-g*EpQbbjvCL1FilGUtHjiN;P*+Z8nqZz!0r1^*_@tbbL7k9@&HYds4Bur71 zBcyF#DI)IMkSP8k{T;<}ND|FWtr^1{MU$?|M*q^%L41akQk7wbHaJ?(mxzP(^bds` z+1xi_U)sl{*587B*_DW#?o#H(+&H8)P@|8H7oul2i*DkM=x1t(+f1cey?$Ml$3&Wm z{Ej;KA!+q184WEHVT&7<8Z;xrBf6U$fw$SB_SJeu3 zC9FA+##VHDOEWAjCxJCI8vOmwT?=X&=QzJ^v{Sc~mjRiZatW*X>vBYsW7u6;sh;c` zL)K!~D^niyDmcBL5%S^`^B284^te}RCEd@{xA$|z z=Ub0io)gKw%=x}@BoboYabJagxZ8b+>M;B;{>c4E_V5#$j#T*Nt#2Rx7$)!>-UyBL zSLa)zULtdRb5|?#&Nd_G>u60{zX$cPAqg|O9oigsjM7#g#__wGu?oL$dTpWhOyCK( zGJj`Udiq$Je=6m>WnJ&gic|%WAl+v++kB7mywuOQ5$Rg1-m41!EEp~f%`0`O{LiLN zLXR@NGsy-*2lxiaJt~^MNSlA)>r`vgYcsrvJJG*g_@;|m!|jzpjlq{8zxST^Bwb!N zkTq~Mh|QihST+c{9`qXxn66c=URxVmOdb7ta=klx zh)lA#D&iwHUv0iVsS8p&e#uU?C|W7pE*$Wd>aAU-k0`{fpgg3!sBG87!}_*aZ`p@3 zvdS8>h1xK+(^nr}e#l_6t2Wuoo{I>wS}i+FaWf+8D!Z!}L$4wjXRQObc$0pJtrA*g4;Z%ifhvke+cGd44!yGMnm= z?K$Iu9!YRoeU|BH;T$-~>TV!wB(>-cULcjJf1rD>{N5Kg%AC3PD1-C&v|`^S0&V(1 zHjRfVw`botzVn`Pk@2c=;d01xUwn=mQy=E-bBfoh?X}jo8?+eIct=;YT8*tjRVh^4 z+vV6aR##T}%$j(0c+1QL&UDziv?a7Z#TT}>Sj^vPvGZ#8hR@8--`M-G*SB}NyRv&e zhb#~siP`Ku(WkW`e-TeZTgsuLFemRO&lP|j|1l)oTRytA<K7l!(qki_d;pT{62W#uo2IbEn?^eBW27|E>_V#>((@y*(Ryd{gB-tu?UF*g7KhC^P z&zuFaLdEBsy`-mPTDXIKzbv6Xu4P%5)jWLS7gju^H#M;0H}kpwbKmFTDpe~Q&wF{d z;j)o0_r5LujQr)dlIhp?<=1bseVdm7>K@(Wnj7D9U$5xa=&tEXhH!Q0Empc9S4n;x zm|rvBHtQ%Z7+W4QLakwhv3Y?%Pd=UyN0;ZTa{uxjnt3b5bHXy^rv85LzG4H@ z$hC4~7yb()>5qHqPu^ZRT7Iusk@uI!e(ahQsBEp_8a(}VpWQx5m9t_{Bl|I;8@Bf*LVZ*Q=>vSDsYRXmHRBe3S zlg8hXAf3B5B9QP&x1GFd5S9EZ>qN1nfg}}{#-v)T!>L9u9kS()*N$Yp=q#MGH5VS? z8^4osVfJP0_gGr4esIg%(E-ZA#eLf^JN}qL6iK^oyHiiR0@7B#lOVA(r4+E%sX&B$?IGNZ%w!& z1_f(JYMVgK?Q>J^^V1iVd$rY9!B^hXyR|`&UwmGauM7DzL^I94@jPs?BP1zwXM3-> zm7J`H{9y5QCtug#m&+6T?N00S#sg#J88rQ6o$tfoGHlw#rw_x&`#Lm z<4XK=JaVing0U{P>b)6}8vDmoqOUs&P0tg^qQW?d#u{eZ!us-O@z?H?;uwdg7Vo=} zt5&lUjo-1hVIjI4ErBykYR?DCGtFIhU}x9Osf>2tK7Bt&S}l^cWM~y~{;dFU=MO-} zZV-Rx0CJ{Uc+rXjB?a7&G``;dm;hg`Ty1pS3=N4y2xST);&=}tQbLKC@L?l-h=@qi zJqNz&i<~h&jX(N1p2p&mJqs{_OP%C zv@tYLaQ5+*aCGr;a+L`2_Wj$0NGU{tQ1o^UbmR{4_Ie(m5TeZcuNn%3^55H%yxjk) z66mSSYh!4_t>NSE$}J;tSK=e*s-jlp{Pn=LgJRtOWpks*m^8mj8>g2!s(R2-P_V@4&^zeDk{kLC7 zC!e4|WnSLD1O4aczvk&0;_*KtJrDTzYY|>h^6wK#DT%w1|LL31RO#YQ* z_n!~{-B3yL@2meq6#pgXf88YrT7^6uN~Ei)W)VWXn@j%DUTYFB z*uIq<8*86#QX~)+t4XSUP@tP8U>S<1prq^<|RYF^(G;wDw25SJE?9=XO- zKuYnTD0OiV58l{62z7%+Afr>ZV566`z(W>!6S~_-b%k`bhKW?vPDZEAur_#$5dF0t5 zfN)qe&GID-0Exu?0I( z>U75k*HIRr?pU#tT*D*c9FM*-j?)PHX?Q$AsfcRBrM~dsC2ca7NxNho@NR2t_#~mL zt;6ZM*EF$18fA1^o;+8X{JvMF$?owa8E2@bimUPE1bJ{`d1f#d^630ZQqc;F9)x`w z3n1UPp}D~d#IO3Hj(tuedTN2Uxd_8!bi!y)F&UI&w#WT} z_~Duln9#Qac~j9*0*`~TtEDGXt>(VP(g=r1bbBRvY1C;T=VYE#iRbWnj%>N)8lBXu z9S&;eIyr)7eyu3ty6~8zjST=0fARNaRq%DX>92?3VBFTR4;+7ZS^F*m|KrQY6;__L zj$b9=PJ=bSud#|&)-kFkBrx$9YANdL6_%+qTV9@MvngT(p~=AtC=pR(F;sa7cGn>Y zdwiL41`P!940kPGg7W%_1%Wu!2Gbb>5MO8Ql1)Kp#tAGcM`Nak;kzV}S3h8if#4!U zVhR9`L!ZVwzoI$XsRcoT^0)~?P!d}PYlNgW_~r5#_HF}9YGdUrtKUQdDC}tk^Q{69~ z5dLSkia72J1iKe~U0N!On_juOeM`|?nU0HwebAU`Fjh?fDm%mNz{evwlxbG_zXn9d z7;yR2seRQ}x6c+XHVU0hfA)zhjV5$bS7-K!{JqkmLrhe1Y{y-5yLvI41Huc$$}@l^;IfjkESaaioi2B?NA@`|9Db;co1FfI*dTzo9BZu-IQ zhGZZ`*d%kwL}?@#w{|QEgJ7_O8=yf5uJ?-#oMgiz%k2#j@K$%P1+b$#U^>7~~2&i2GJ@rA;WxJKcL6NWCRDE|vMH>LUWyw^(R?RVN-wKX==4JT=pj zua@M4MbKQ@I9zqiioA0!jdzCU8LTf8Kg8R}P}5+Y2-qG&XEN*T9o~T#HJRULM{SFS zPCk;kNC)&Ci8)k&V3id3JkgX&UJT8THf94G4qdF0h~O48zB+Y&O1~V0G*f@O7q4 z4?*P>orye!$w9c6DAmAz6-!H#JM z6?uUj)s9~ADBkp8v!=#Yc+Cn`5N56!S&o1XOo*nq23(ndx9X131WSwgc{F?mIAi>N z4)=ohkMwT@b^Iq{=xmVuomI~U2qRC5xE_MWHYy0H;Lk4U;P?TgPw_eE<9+_#ePG?> zY0Gbntd0KD%8=#-eCdW?vo}LIMcl2?M?3tgd>2cnEBWTRfis&3O&#SGtt_0w>bT4* z!>sQo=)>XbdY=o--*l_IWdupB#y4QobjEBll_P@;VzX9;7fmBTw`6ufyT-3B;Rlf0 z5|P-o;iNftEVgIV;_9)+>@a*%Y81D;0Xm}v;{7FDk1@uP5*yPX7z9nQO_&P)*YN$I z1GNf_>@+p1oM{n%4=MD~)1ek4vNyZkh8ro-N+6rMsZa?>(rt(&3cN67O%iysSwBvj z@8=}SsWJx0X_@#T>xxYsGjjRatqEdmjVzJ@GSwgFw z=wV>?`F8>;U28BV7#ke%0vmxJ1OOtj7z|<iaK7YHT-Yf+pB>x6vA4U4Z-X26T`V_9pI`LcR++e`OEs^nlOwHiHM^0EPf8B`0{e-ax?Vs!zefY~5OigRK#KB6 zuiQUDqZ-h%JPQ`1HIGl68o>?U=z?zc8u_J*3qzz*=}wbc61BpEO)y1<8q4Ec_=jvT zW*ROaY8{7E@N!>}v(&M+I%z3=s>D@;uJgqPynwycr!$}33+rLYsCo^=$6qY1&YyEuG9-3!O`gx=|}@@NV5`W8~^A|LVAn z2iR-us4~~9OegCd72@D;sA53kQL7axe4M~YEHe-v4TV8ec^LO(w=h$~1SF(;P7cRe z{SE6q8vqsj;a^Z>tCx*3~|aH+$edeKD1v24ai$JT%_h6j22D7g=DdTF_UL zDieUUBMpwN$;dCIL<=0GzGDEQKO;OBX_k_951ET*paOq9DrxM%hY4nS{l5MqH^nd_ zZ)&r5SqDT_T%C#(4ZV+EYuPo6-hwH}@N+^mmfh`9it;LBM;X(R`6|pQuzNG1*b6+4 z%r28j0|A7np|1W)81J${K|pl1FCwo7&u+kR*0A=_UvLiRSJQPTSi4Hn*lBLYUnZcQVx9Pf6Xg?SU;60YM1$J_?s zHrJNQ^52(|Ud3f8>z=pl8h?!cQhNuwnP(}p>+bb2V|xx?W;`!yJa2Xk=(VQqZ5Gfv zfQ&l#Xxq(>=P9X+tyyZf{?_7_tWBYCS&~)@z!~9*4M3F19ThwZQ#uT9 z42VHOq-!rF*Cnw6H-AEOXWbc4ZSMS_Usy?RC}dq{lM({isuH1G>&D13kC_^S9**No zhp`-^^(7b5U2|!L*6)J{A5Sc~a(x!bCo>jNCAt|sL?1PwOZSvQf&aRx?8HU=VF5tD zWeaMK?kRE=F!umsO(bpsID@|P)#h3rO~&DVl!8a6omOg%!E-CdE0S+uPHRUrLW~3@ zSx4ZHSB432oe<1%KS7lif#nZJc6rZwig6qM6J^0lG=N>HcH` zOHkw^ohfJ`yXG<#2WoDgvcs~3%8FwxwYHb%M&{3uAGD{|L=r4%<| zd?A2r?lpd|lMGCjgm{R+*&y}k#MjBF-oAsQV|e?@uxToAwjNn@nVWK-0AC{dpzz(_ zI~%ZFxuqFhC<~`xBu+enz>J_ENZ^9y_AzW%X?YsCyJ}Fd$n<6?xy)U+yi_eF?y<_a7qmE8nmG3>d>se-K5G; z)@yb}-+ao#enpQm9w?E+)=>bNcso%Zil;GvN*nO3Q#`?Ex@^E+482)~^-UJoA&p4H zqYvoqB*gHcfuAon=>Xq_ccqZ-J32ssey-Hi9M>K|y6{_{c}{mgKQVNlH*LvOKH!;t z0qe$(tgAX4g`cV4DXFHF8ijr>pjFOM z<%60a;zGigAHk1QrbVnGC>qkrVD@R1Tzz#8z zz1DWv5jvS<_}#97hWq0mA*hE-=rvedAohgOj-T`27$U`#(?=oL@@z#?(cO1{1e2_^ zyaC&uCh$79%j@{dQ$fY&U%&n3cZV43;OB=JcO0(gFOhuy{MctbKCK> zOXRN5a1DO-y$;JFewn263@>fJgqxsfx;c+8xarWbe z44)H2i8dSQP;M$!F`ATbdL!jxz7{$I&;mfZMS9COSs>BNWUP>uwMZ9u0B~grdsy!7 ztdkCHTAcX8nW+>sGf)dI6PHN<_t&(bo2o@sP7{m$L}{gzBID??xlmzZef3e61E&#^^1Cy(ZonyLO$q3wAnCA^f7-U^)>0?1a!Z2Kw4?;&F z(Jip>0(&uztp#FJux@R)Djx6RG8BGgyt)L2X>OP_ui@-lws6s$8@INb;X+;De1!H( zE}mY&av3j%SraHNEYWAyRnP;GWobb+oNs=&f1D+nYQCgn_iaVBDr5iVb)j;ZL2XbG zMWlre``hO{(I|P$yY!)$&m-2 z9Vc?@3KE7LT>P+_0zqxb9r6HC$&ec@H!(d%5rTJAROpunX`ejP=_HNF&{s$;p?-(s zrhe0)%1uWvoO*3=LOzNzh+ZCDn2Q}`36t`iI3_7e$u!(MSurtEv*wz&82Q8(xgD?6 zMc=EV10P4P&W@q2-UKJ=zVC1{efMyBifOISy;T2lnSeI_(xR8n#7lCgE0XFA_~T;& z+6b!taljmEfW27x3(;3M7#s>bZy=I!sKKD#4Uh`{1oHxCPl%eR4Vv8hw#(RzAj{OrAPkfTHaZLlgFEexArpMmw6M5f}WLT zKZ7dgc-4ObqVoYax9c+i0|U+(7ou-qE#^5BT?I$4iV1jr{OSimeF<2pJp>5%cx?lQ z?JDq$hCz-Il$SbRrh#yr%jHe{B}NtqJKiyF0o56E|6RSIHSK!i$FD#T?8T>mUP4mI z&Ann*u6pO9_Nw{tVwiQ}79_J?TnTTgEsZZs)?5)ZMCxipY{xe&f0{mum7LRapUuvy zb}F>CztRm_&C%R^Y_3nK6e{z2Tj;(oF<+-@ff)C=MR3!k&1wu4MCy;nr{I|q$kEsM zZE^O?9%zCQE~yt`ivYkea-6}Nfe(z#n_?ZX0d2ntr&3xXW@!*0C)*X zqh6t4*2@sj9lm{d2QrW&vR$V$&98n~LQj+Ui=>Ijft&3!S(U=<4WfmI=Em$YX*}27 zE5yodJ~TH4Z(BeCD7|rXLmF_Pxwm!uqeE7oAV{n$Sg995>_>^Ho$&y3Qco1CSIsS( z>#*-CMV=y&+@QNGycO->GhW>1IToY{*}?mRctb^Gv2(|m;*yQh=bZV} zVEfihYNem`>a#dQF&bHYI<*+;pmGyI{3^TU*f*IHXBt}AH6wz;gw3rY6e`MWNNUpM88+>)a2%MY0;$8TE1=S4onP85uz z#n~gu@Ds%=r3UPMAUklgY~#~|qx&2jgt&r5|0|d@?qvLRD8kC{S6laUSUkF_Fpb0T zE1a_{9KZZK|I(;<>q-zoT^Me#&g9Ui#9m;)sUH3luKNHV^|`=6x$rFWN8^vFhA)?( zK(B;;z`!$Tp8u1ha%+f(M)*FG8Sq488DO?xw&6F&)Xi;X(*Ma>gH^1MI73GVa`MS3 zXKFCMJV2&n>9%6DF_&U269{c#XSPS$wCBPu>^I_BoU&Zl(>iw)QyRfYB@wF=q;Z8h7XeYpA={L8y*c>=PB!sy zzcA-S$5A!GOdy(C6L`%vHtd4;uCTL4h0KI087sKxY=(F9^pi=S3UlPdnfH9lUg(cI zS=8I}tq1wd)AsHwdCK86<847Xw0m>ScEy&JR?*%^%e@7Kg}h?6O+pq{R1Zz#K- z=$m9)SU9xvPr2^h(weW5XzU?79 zKwuD;nBif38`8W#!0TVq2d*%WOA3#K4hw^cCo8AF}Z)_x{g)=!U^ZxueF~ zt`Bm--xFA1_{rMH)9+Z!ap`PRK#4c6Tjo80Ak3fgcai5Ot~}M>(G=Dvvpb^0#q_%#Yw zT=C@K|F(5wLPu&tRPc&^AONBguzup#ij16}+<-xFG3(FMy?^iQM1sKAR;hSXh$3*> zKvYGsSOfQ2Fd9)dS^4Xo@HMBpw z&Q}H@0I|eG!Whb~t@wZKIjIh65QRmRim8We6|+27IT_K2AIg#Sc_|!9F3PCEAhgW< zP^&0`>ML0dQRi+M->|@QwcwIOEQ!oAV3mrn-ZJS&mg^rkRKK8%9-UD~XnwfMtw}Pb zIw}l$YWyguX+>EIO)O^?$rL#A)I7Jma5J9U_07UT`vDGiegOO<$0d)_jU-FQBjqUS zc$2OY-`cD;GTSEmgJq1!7n1l!3K^a<;~Tm`)*%K>4Gm+~N-dknam9a1UR9pL#X?<6 z%av8`a2|ntfsCj=oA-h1SFO3sug}I2=b%aCh|$D>%YZ*5^t0(KUwCQN_ACWcV#8v? zuzxggmhKggBPukI4yNR6yZIG7oVTQ2Ceq#`A}cZ^BDl@KDns-`MQ}l>G9k)Os(8n9 z7SSm5slPY;x>DJ*mIdm9SLe&8SkfAeu<}YI#}Ta#y}Z8rlBcT07CO=up@>AV(L8yv zO5sA`yPv1{!aiHtLyuo`zR81;aGVms;7iIuOi<3hE)Kogs!3vG)aWU{tjG6Oo2ew^ z4vg6A<%o_b+VI90q9gKUa-l&k&TezXWq?qRTEZZ=d@msBK`YhrfR>EOl35nzZHZfP?g07^eX5f=VYY(V=5O6iMCvx}T zgOtT%HfG}I9P%EiHSPPh1sd^ZMkCT9-TOlf{q#|fH_*#+`cTFH*MxPVFaW@g{Bi=H z(FUKYjFa3;r*nK1F$*`X8kS^D9Y?&#y#}aa^`5vz`|@lVaF~=3Z-UKi&$ka=W#^|H z$?vmF-XtaY%2GbCEfyw2meZ$CUVHXTpHuy|X(zU*(rrT9CE4lEr>pBMk3Z|2?=Q%% znliEI2Bo^dmaQQ zc~-}8e33zzqA&5>ACj5x!l~1w=}gKk%WOhPcBvAluZ;FAVDBdLWc2Uo??jzBl}d9f zXmS;d+g_=2edFS}E)>h8IQhehWHQ`CsHepto(A{^D^ybM4>Y*+ZoOJp6*%ikC$o9X z#z?%V>pD9zA6_AKl zE9oHXc!?(6{zLVL@}bu5L@&@mFfuyW3tgOxHLaCvC^on<(^bfhlPhH0rq9)WSZv;@ zP#Y~JN0X{jR;?=PMJ^RX6V@rsYBzvZn;sPQ;S0U=~6`@S=zHF_QFI z4zoftjjti9uamvXdsujBHM&Mjp?hYpAEp)isS9qgayk(e&6(jhNwdlL6;HgV6BLMf zZs`S0G!|>V0WT+o2#;{K1#UH!;Cm-c{5%3lLwJK1=^33^>x!M;CsjuK&g54=5(_8gk8iQ;St*CR8y`zo@x!__bkkVaZCWO zSJ?Q^6x#_%ZqxiY>5@iLH|`>l4EG+{AP@3M4|ARHfu#Y zIjO?D-UaT?e5tGrB;sQEZ{Pg4i~jqP@5=i%A|gxTNt^A#68-X3N^+;=`9euj5a_q+ z(iJ>}{4+8C8la5vTy=n~m0N|_E->}pYHv^xmPEmi1T}DCsg${^S2`!eMx@fllY=Eq5$+8sv_)1bj!3=6=FDIMN%9w1D?A}or5f_ z7eU*3@z$Sd;_aQlK#$n`z=yf?^mqBT%P4Ls7~98~DiTDXmfvIVI@rfLQ>VL{!IVbV z#~dMMo7t8igF{lr5`jW*KX?|Heb-UF`jf45@&k2bSI#`@bMeBOC(X*+|Lb`U!&ueH zN`ueS?khOJ%{yy10trXCi10)_Uj@QHlQgqHRo{2)W|sVpu|$Lg&hQm|Poo74ti&jJ zbZU~6S@Yc&kRuUb-g26?F)~L5QrcYI0G5Zo7gq?6^kqy2xK;!mpRmx8X82YBJ0+X+Gi z26;4QQ4IvS3^t^KHB6+M8 z^XaiH61#w%)mQ?7RBrbf;Ds*cn>-H=b055*Lew@wp4u5h){+ZDAD!-ZMxxhcTU)#< zZ53twS9ct@C(4`WLjIzlstyY9)#=1YXL##d79zlxe*zpH0St3s^vTVJ=eKC zC_(v?G7UP`{jw(74)im1S_o}wF!#g~NQpF?BrFWMZhgJ3Pj_-&lsn-q`?gs6)BdFnNl?Vqr@g1;^>b>Kiq=6__0P2&?Sb zXSpU{u6)#=A{)gZprIU&l+cxzhgPEDL#6ue$B4^J!}Vc0$G)Muwe$zwdv;H5PCc`B z!#7OfAeC`)?Wo>tqzHf0l# ztbO!0pe^&ge875a7EPm?;z3hp9$PKO9Tve=GAq@AS2o@W~0gxz-%2a=Cll<58M>^WtR7GUxrY{Qo-rB&I_eVpiflyiZb1wc;HE zh+>bTQq2_9Dl@*pwmZVpP%Oq-eln9D|H5IC2765bePg!LphD@RCJ$aMch06In8zVP z^Vc}7kWEF8J7t{H$RnpQ?Y%O-+Ha{1qg-~U47qa&Xf;8eE>(WxODk-Gr6f!GlU!lZ zry_6iw`3x0pLX_b-?DV9_GoV@$lVeVG`=w&d6iU$egUcDvSQ%Bqty7)t)4-;(gJ*2 zp=wSUNV%K*c&o#9-b8+F@a|79kr%j(yRsL&I!?hxMs|RL+d!YGXo)a&?;)i>} z99MJK9cR8)YRp@m+7*~PvHsM%nzH-U1IJ(aqPHtwpKedHc~;$`Rq}0@jcs!X)C1UYvCZyyxz}3xvr22~j`6@|9_S;HW}+O) zHa(j7dE63@nJ$L>q+Ifdd9%(r20}oOffJw1!KlqK8^ZAft13Iqs0r`)k4AW|@ohIL z6w~HINnaPEjlbReQrJ?&R^>`KOtZ{7PruX8s1ajjcSTe*#$epCY9||8R337&^!a58 znVK{0CsyIHHbh0iacph(8%JpDol8L3hvh8;xHAaF8JcN$!FHF-JR!)K3J>yy7R^M zX-_$&v%rH+Ix+L~l4rX1=)+GhV)6=H;E!!X&t~y`mU6rv&pWo_ii5uVo*&MUl^n{J z>X|tj6CEI zR)1cH%heWB_grrMgXVtq*ZRY3Cy&P-OTIEUR+h)rx7fI^|H@@2u!h<;$@Y+e&S^*4m`4zhNf(siVN@TCRpZM2y#t5h^TTIr(buQ01 zn~b)^sSR2iNVGdgF4OQo@Pa+v6D{iTXyj7m;y4;@ehjKi=jt>^eqLE6^a4s&?2;tyb0 z;OHb~;|VM7c%(-PkGllKYQO_DPacHeV?0l_NM;#VKCc*2f{ZZ=7s8W*39s<-Yw?a* zx#tO@u6Mgo1C>2fcBgbXJ}s7LIMUwK`}#aVk3PFpD>2zZcO>CT_dZ6P;onuHgyH;0 zN(^`EF19P<8-FR>H~1he48|Uz595K4IV16xmHyt4^LEjm=9abuMocdYi0x?J1M#Fs z^p?iGpzWpvK(@~`ets#Moa>F{n}eqDUkPyX>=7>EI7RqRg5TcWdBuk_`|a$C0(piT z#9ujlN|vF3}X_*ahAQYLge*_h_kv-6tjZ z4~}3dn(ecV?i+K*mrAI^I(`)DN1k8b))ERydxMY{ag6$ixnhPSkkYgP*dMJe|HIg# z_)KK4A@R5yqnq26wRA(bsjhPd{k9)$87W!I;;S}6;gEfTdBhqm>bU5zIhGZ`cQs!f z8W=b3TZ2?>*bzhKEFnG&a~F5Uf_Iykf{Hyx+Po}lqde{^WCW+QO{5U>Uei0UUz zts9d}d1)L|T-7p=vCN{ZlA!abUX0!Led?6{E$Ah;%9MpWU;CpsEkUdjc^A*uYgyGk zwM;D0%`2?_EI-Jp7MH%+-Md?UKgV>YO`3`nhemkzOj-HV7{-TTY^+&Ic z`#GL3yr4;??oZX1q(jW|85}?kjb?n15#6G-Z{9$qbAvC+mo_$X*>M-GK%Ms5z25%D zN~0+qPE2PV!G>Nz5pV#l%BpHR1I|MiWC1>yVeEE{2e2)2U4{KIt%6U@Y)XJ7n!beb<>U zlW9&TJbV*2!(I)q}CSIb%PKBh4b1 z``%jHPh~D`ujRaz4jU>D+R4-(`pxm;cl}cNl5u>_^S0iE*d^j?%aV{z6)(4g;V=7h zKyssw_S;)Tlm+HO9Xal+m9<*x zuM`rlyY#1m;-4?<_Ab0urB#coI`@CIc_MUuZ+<^~4yoxQ$NGqrc9US7ebKZ`pmMh0wJ!zo`s7 zndaxazBTqx*l>hx@+=*+0Jy7|F1}i6^LW;FzEiftR%ai~zZk58SATVcv&s3Y^7oZR zrMC6-ml@?Myz;(;+)uaW)9HH0uJ6@x6EFMxx6aZBPaMaO&+4rE9mmi4Q2R9{TuLDT zKEoH~CC4xS$qWXmh#4MCKMv@uc7e-$j0|h3;CdN%&RIiBnw-DU_x`n_FqnuPOiW7D zL6VN5s94c9I9(03NR&AwM@5S#J#2t~x9y7!+bz?0nu7vNR2m8U)#=Yast+!gx2pT<4I*2P1(Hc5Yl>kIDc%E zWyM_GO)wEANo^EHnXiotdTI!W8ZbpveLFUiW2p5 zZ%(H7M5;a3EXnLRUErW;o;)8jr{P#k2 zUAtpnFA-$X$P7ZZ)HZv+x0F@EDfk&Xzxn)Gw$i#Kn}%|RmTce(n1|re}k??Z@G8#AyrUSbv1{Y@=2cFTf6Yd;TeX+w11@D6DB?@~= zBN7$orq+C~S4e_&4E1lbK{x8uS4)A)d>o;x3`f^{c52$6@8X!xX2wOd?LR_r__^dg z=yD>Xh(ho#tBNQpGV6321nfG{6txO5ra5@8mWbJQU8Rr=eq|8NG3yXRvuxeSeMj$9 zSV13mIdw+TmMbnriCR_5pY8W?C{4VKqq$POq zk@5RUC}f^S)>X~!|Z=GTh9E4{lDI?2VNfGRtP%P>+Y*bIN^l2t|L=c*1KtxY_f$jn3c7i@Le&R z>CW~H=|R-_kGMKUig0&ai$zGb7sUg)u|El_pi&2HRkVWI%om z%EYixscS9;%53*Ka|{=Mwn%tPhWln?j(P}20`3(KHdd*m_#kgy693+D&%~o8=*Q8{ zK42g1WfL;5()iQ*Bk$vFUOq%=r?ca+R^@fCC+{S$*=jYM*>?JKE_F(RU$PhEU1eHI zgztR;@f!rsI9`7AhN*O}=s%tQ6L2UXyO_B!vF{FTLvu{F%=7;r2O`6P0(Wj@YQ@6*0_aMmyjKx7d&K3kphtgUG_CY*0sP2i(V zg5Ew|xMxS>|0g@+DyS=~TWqU< z>)Cug`jW{Mom0p%vX+j)HS~&c%E67fpw=f97=1*R71MGJ;rw9u9G9X!dWh^t6M+-V zh@`Z4yXCk->z^pBF8iW%OJkE|hE(Q%4W_jR@7*M9#Tym<~QF*y*Bp!Kk}Co0OEuXjlTx$8BOq0;*DC9 z9J%ZaG6LUzpJ#MSCb)C2(Y{YsI#rs=Y>9@b*6CiiYrX?b?Uv8C<`~rO458lY4K=A% zSRNa0&{P||C&TyA^X@bKL|Wds6rLGfQu<&oYJ>9a%)+dE4U4SxdJW?Wl_QR*Nbc>K zsT?`Kf%`$qB@V$4Tv4kd+hbHKrJq-#dDhV3_i(g32?|15_V_wy%bN#Pk{3uQ!s`rk___y*?)AolRuqQY&JHP^)KIw2B zd5U+~5qV0LHZI7mOzx_z@o-3_%R2%peWaFsI7BU5r@{=i36*1hQ_DajMj=F)fSk%n z2%n@M+zZQvp5#&1Xp4)Xe?wQBi&3klNJc7%n@xy(Ydc?p)o-lr`o(yAWOD?HU4-RL zSMPNnS>y3YTWi#Re-ug+PD+$^-0p9wo=t*4ZNT8x!S(ZbKalMzd9WvMTqx?9LGyr= z!He8#DRA!U(t(GGl3 z=iNq;cBQJiCAwJdw|S~ZyNKaovfz78CL=Ty=V7n#zRHjvsO6`t?HRrcd$C^e4%QI>MvgSyk4w?{gmGlBp6+m#157ic}7U|yj(V^j{mKHPTdD-HUuY>wE{cAw< zA=< z`p+Huf4?!41olU7JqhT-WHiBhz#u5V3W(~P!rW|c&`eNol3&l#+V)8)WnYp<_5Sj* zOuxrg6p6~(++saR$8*knahF7;QCtwH9$}~~gBnz{rBYLzt&X4PN}3!xkwR5%mNb%fxSTQhaf=$ zhd{6CmqR>Ws4PbIVvh&tKTpnvLv!@rKKITPN(9r{CC*N~?X*{nXX96 z6>P}GuqzCgreq~O!IgTAMo-ngnM7qJYkPcA3`ukS^pCUsU!oxp4*}K#<^l`o$MH;_ zt0`uQheWy*K)TX+#Y-3Sd*hsj?Q%39o$j!X`~4?63JkTB*9Ap@t#q;5Na*OnjfA2}M!O>d?>u;lq$&Mtz zrhqOPdhu#Xb%%ts)Nvn+PWp`|z+BH_(em5m7MtqZSbEB((_J5p#P%GA-=qN(sEXkc z(o3jiCvDgud8DG>VtG|wzldUY<)HHz1F6ZN}zR=|)E3^Lfb2qB19642$r!SMF?A=yoWbaJ$~qBgh0 zE`CfSdXK0A>-mZZyXv`J-tZMs=l3Bpu2{g<=-N9CbfSOsTWU6`Ct6%}3-TWP5kHXW z=NvwF5<>$pSpqTWKSTqaQ!2}mG!|J9G=No~ib>gLxG(2J=X_K@&_JiO)C-E`l^qVl z#s(2lf0xV(q!Xbt5!(}=8!9e!%95ge8ODDS5vuT3V*WG3Z|XjSAHy&e^QHc_danW{ zr2@t~)ipH27V7pmTO8c?0gLJft$S=WmES)af1>`y`wxS!NI+)NXR^-OeiF(Ih`?iB z1vSuTO?AV3Ym_nmwjDE|9jvjC{K z4o_bN8Y9<4g>A)djs5eq8SaP~UKrcoY&~^E5UxOzt>v2GkLguuyHR+#F3VK4Zwevd z!1;cZi3l_Rf0W279Yr2=QGne~(5TO`c!B1}Nini(djJEjSGJ&|E~uBX%)U=$7F>Y9 z;%0lyf6vu?bYU81rH6>8e*vP#*kBm{AA9DXCiD7@uJ_*>?f%qOnB&B(KkWyV0N4@w zAi*HfAjx1>bwMd&BEjDo7%h5i%N1S~$^M1_A{4|mfBt@Y76$MOVj#^`4&|0o5mk9>*$IX;Z~2@e2Suj z8Jn!7N6rcn&i#M{dMvl&Wjma(p+MK_xS*zzf-lAdQRYMT)x%jgskawC(vDZ;x_?6W zLqg#$H9C+xQ1Jn|Ek29xez_Gfo{{@5@_c_!9EbkP!MP%N68}#%XSo0Mw?I3&0QvRr z-;OWN;UhfYJrE9LHc-`Q;{tG5;B+pj6qqvV3Q1iziTi{MgX6w$4&i(I$l@aLeWIEv z75ZMDSfpUBra|9F_=Cu55%iflsEC)hz|AUr!YDpRlk^r{?P*fDgur9IW>S#Ggl;Q6 z)@w0g0Kych=qIiE=Wv9>aL3zLlx*yRE@pLN!>hfW5QGCvK9pW6Z$SUqVy#d^uD7Bd zrdU1erKDJf_DV(AxeS} z)~9XLCN*wTNBtG&$%2Z~qBap<#`s8ks_v`6H=+SgKYus8f%~=cPn-YGrD}-Gq)T`J z^9crXFMRATLkh%?f%*N1nqB=Bb{=Y1krYS}KmigGL4BXcz8o-w3wm1=l?iKh z{F@H;w9ETfZbJ7R73juC7%kb3h0Do;LE#ec>|{FFI+&gf#wb}eTOBZP+F{K8eQSr32JLGy+d-Y3N}ITrO09w0n-19* znUe>{KuJp_?}R#ndx^yS#KmG3Yb zHSjR0J{UcMJ+PmJK4&6N0bWyThUl<{x{$P|jJO^5vl}wKTfySF2uXiFsb@b;s$yZ3 zjryPLPYFFxA{syR?rLvvJe;81J?rlw7&BhrT&2!nwc|Y__Qylk{Ut&mwK2lu%)9ez?rQCd5URB3~n&VMp?MF*=iCNU6ew3`pg1O8@yltTM<*F9-+3 z9@;H_IiY+!L+`Um;%nV2Oqh8*?WHOu627ij8(Ga&)|)=hWQ-R(j`5#J5LYT-%jEWW zL16+ne6Zoa?rEY2q}AkTlz9XZ5J7`dwm-xPyCU>q0DE-~cb|jJMGp;N=Dom48%<=t zOuxjMQI(YL7g9>|HF}&8^1M%WBAVLBFPJD{>6Ro>c-NGID-s?NKskO%DGpDqh$K;= zlqR&uVY!g^%W#5G@NOop4O|~vTGyebmg5ElM}IERpn<<%o`HCzu-R7?`<*rM4rU@0 zcTODK_nYJ?NMR|e6j;!NOR{q${E;g0YIg|znp833dF=8GtLvp&oYzD(zc@;A%&H}8kb@}Je3q0xphuNsU zKWm7h>@rCIhnZ_lg3jNMM?IrGKs4|X%E;Vb5_FXjokG)Tp~41H{d=^zn3!Ujh%KCo z&0#6Mh7p|I8tB;c8E{FONE3+``E^wCOeCRR$FR6-GbSS|RJRd6BmUOu+S_&|j6`ec zT&OIHe}^mo79yub3p1WR+B8EOTT~w5(PaA=*9!@u!tGql!f(}aKjAi9Vwry#D2_dz zOb&l2Q9tCYnVVn#kmo}T<3vMaQ6hJ;kDVm)HSz5{wJdDlJtx_nifSOjUfI&dL3s1?$j-3Ad2!Ejxm(S++F@0cAp$*L^?2^VG?(GK} zjF+?sJ(O{vVJc+_V0$eiK|*y2wmz`gx1 zPl_KSJR2SC3T3qq02RIp@CXzY;IbCee+-5o9$S^Odot5?Yn0w=NF)JWsgkm2T`6kIJ9rWXNIbAO8ekLy8&}5BAuGVvwcWE3wC=Q=(B$?2v zI@;O0MC!y$hg!&fez#`tPT)AAL&49Lx8$!s*R$f9F!628mupQUa9ixv8^!lZeG5Lyra`;_o5=??-Av841f*ASj1MnN!d{poaQ~W zi9okedyTq){lSx0Oh33t71=8&2@ccUj_m_FvA4_o_r!EEAz})*{X$hH)qb~>9e7f= zK4m_aL&dWfX!sa0I%mzlb-@RuO^cku5pg4O98Fb|K*84Wo@!$Y@*B}M9MYWb^5FV;)*zgJOLRA?6fg}j9F^qKlQJZw#LJe*GF zz`7kw6^fjE^vd7p%|>Zl_0p{D~ zeQwsuni^BEr{x*PU?{6O6KY1)9PjaWAdqbNuC8VK^gR_hvBGw`C3WZE@MSB#J>Kq5 z9o%oke6(#GMk4z&VD-;VD+3FJ8aYr#28QGK-c+Q;1fWGtj?xi`+w-Ezu){8srJ&9h z_pq=S1CBP3^rRsFr)1VKpf<)voA2f>XhU+_?W_k1j#Fm|H?{oDAiHl8iHJ8 zhrOHATCsn5Q+Pgb(OR!xq>x4Y&EHRa-l4NSZBD&?sS~m}asf9{sw&m$af%0!b8Gu1 z!zra3M6J!_T*nuV6C%MU2#=PZ-k37HPiNmV?=JWC6VXhJe z$-Sx2$=VFsK&0l3p1)VUSs!?$d-sT#$x^(~*NZaj(Z&TIe6pDBn|I>QF9yk%eC|H; z+L25f&XhFv5LrC3*?#}Q-4pZztyf)B@7L$kk;|LLQ7*LU&XT+EZe0^<;nn*+fltX* zM;~p%$YV%~jgt$fcNZ$ZgIM`|^5vRNQ(SHOzHr$XeU6M>VQvKT9lq1BA{lGhi9R2j z_+cc&x1#XNaxM=~9gVF|>StYd7i|QqFsonCCx&W(PL>;w4B@(z%5vExnUIMmZ=ebF z7v46wP#=&y$v0BhzRRc0>F|M+Q+MG=rCgj50c1L>zW>3$FrAL(|S;ySUj14 zXtn-Ps2poM)giE_=(N_u5R9s)>6?w%UuzNNrhkAM9gGIq1`Yn*`cC;wH{yCR-6yB5 zwP!NPj?0e#?(oOJ$d`;o)v;?(_tmR(xdom=R2v~S z!1Mk98Wzj7$R-F!qo-Po&9SUXX}4Gpo?TdGXVI}5BEKe?DiX>6{+^oMp*x9fY3OrP zl6h@qLG2DlvN;p7#p<<$$LZ7i+Yr_AiE^nUN9g-@TeFwUwr28P=`m#}+ih2VR*Y+% z3?o-xZK}w^(C^H&!Dhst#>dCR7^*1aR)=H0@w?ilEDXshmffa}9yH)Y{RZOhg+wZA zRs(t~APtI&_tjRxOw0`g70j8Z`Pu<~ z)B(TxMQhLRrkC44EmkVY2l=<^w|g&$`-ew( zTc3IUU!_XmZZOQRVC_2dQ0?1}ml~5kM>_Dw@eC&>NZQ>G+grW83|wdXGtJIz9T1b_ zcmA!wO&bC{w19@o!PJ5V`H2!_lQh|O^6jZW#~ zyh(5GL@w6CJAb#B5&x4Vi)uNSF!sYb}l`alzkW!neqPxCgyBI8j-3+#f z-X`!}#9x|msNa&R`1sOnR*sflq^=vWJ$+0Y2P}A86KvPK4(EMs>Jc+FBRORx<{t=ucZYAZN|7aU_#T#8^nA3b0(AgKz_}&NRFJzTN2gJpDpL5N z$4ShGYmck51Cms8qw&=9e(#%VeMZIW`#$9ElSXnvKtc7jZn72LL;Hq9Q3z*%SSI*q zRz~9ca*{x>9n{YG)>M#4NU+VMKnajX@?!z>x7@>BV?7ofpuW9ro>wQVQRAUNJQ5|h zQ~vJye;B7OCn=asfJ%2j17?+u-h|>6A8d_#6D|DVS{KNd{gUefyWXEdwnU*pHlkyO zG(XP=l~>?h_b0mbLu)L=jh36@djJFrbOn!eJAMGjQrVKY>vK=QOZF?TOIA$IUIF$? z>ikS;&guTTeLpXIuj-0gG6vm!?Wy_=trdfZLW!(jhov7xo~ALAGJ)LpYSgGV7!#Eo zos{_7-E9BHQM^#3t5rscRv7eCZ*&bI?B<*KRtwP#ja=u}C`i>;*?AJe3^NK6`*{l@ z@lvup>&{*M><51puGgzj9aK*^cNl{J3^SNSKy^i$3GX(ONZU^qc$FY@cphq4-BggW zfW0t{4>F)Mm@o8=knDdRIZN!f=E!;l3V}J~@q&9x=!))73{;%d!v#pV-~!k^?+VfT z-(ah;dLPg1)hj%$NA!f9?mS zJI9*MLJ`kOw2AMI>&AOtdw0q=o~~@kVnxtv==t2t_CYksG#uFQR|A0<`MArP*rnZ0 z0+Ar{VoRvj$3bo|eBoBJvtdrZb@4F6*!KbOl+1Hp&1F4KRXyx&F%Nh;0-{2Vhrh-( zIPLULc7B%aNvve}m~62buEX>i6i;BU(Q>*Sx8}AciY{RgDjgjb{ZWDplKC?E7_r)} zE~X{^<}j9kOT3H|h`PDAsV31_Io=svQYYD~(B_DRxpU~V560k*C|CD5zDSe`lVDo& zJ$pJoLwY84Tk_R>D7V;yM5K?6C$J&MH!5q=tqYh9|GMoD&)`CKE&y$sb&z4Ub4S6q9@tDA?bCc#q!tEt=;x69{1coDYu?V&oE^M}J-4;pon zir`2Yt~7B~HxTgIbWr`61eZz^lR5NopXJcMp|Vxu_JPz5b7?O!eBs7J)HZ!;cMT`) z?aF?9joz}~!**RjE}p!&h zs@O3wI&K~!;?sICQu=IFhvf2gMeTs~josYqTTQ*2;w}SZzsuxl=Xjn}R0L(`&B(zp z#_F~=y1qBAx9l4nvR|s|y%d6g8(jh7@i=If%(*2*M!r;lQ?`pr5z-5%llQ~mfWi#C z>=a!y!onAhVjy4RPUK5!zM!)FCoCotBKL*l$|LOcb+YFM*JQml(eRj&#u%#bytP#0 z^Cr6C0IQgUlY--uKMXu&L-}pD$1)xFbJrr-gkJ}MJ|*-xscAeRc{=xPNDaIkkHBia z{thwT9bqAPNn4R16HflTqP146nC-aWz|x3k=N%@bd-LG2+PdFku;`t0TAM1g6kaQ7 zd2#y1|M9A-W+RNGz{u$?JImR=&v>dYQgY@4a%byi<3+h1;o}CDW;n_7WBR$g_Y86c zh45+f?`$J9>R2A6SAJS^@uOTzoR0^Jzrq67?$Zi22ge)ypKelWh8+DM8%D}P&b!AD zs>Al<{`hvUO7aH@^p2Oew@dk3FOOX3v>B!5D>|ROU^`uX$y}D3uzLGsl6%(SZ+o+z zB#*+V<9PUY@31PUDa~Kll zL+y7{YZ{zdV|+aS+dAf}RP){OEr8kMYv?t2KT_lJR_dw;8{4Tj*3*}NW9fXvx`@xO z*aoEDV(AZ>nvBQsvW44)jMeBkC(}CxYY%76=4pwX4Gr`=<&<9bexO}8+vWR#*Q%E( zs>pTtp`%>;*qDShl3MLL;Wav)ryG%zbv;ce%NpF3O!4khYVC~?V9?T=s8NBcN>Rxc#GknH9`}GvHySUxGUfYMRvYGD*a# z>Zlmvq|_9=2zrZeFCl8hGbjs6Qu>0-Bz7`paykJZ;d+@snUnL-0wIJhWy#|s( z4c7f3Acs1x+}j#)n^Su|3Xhc{HB~@L`gSw*4Yd^ntszad=&jt2(4%9U0JXOcWQTQ} z(QL}_317z+G!W;zX3sgY&|v3kn5x(5r^Cgw8?~CCPDSo}{b`|hdjzKa;hKKQ&y)Bv zw7M6IpXntTwNxBBYumZXyj3wNpzv@Yjdv$=-<;k=KC^BHc*HV+Qv{jdik8E&+sHdT zlW*xnf=}0Vx0~&;n*|o?I=LK;CWBgo{yu@RolX@Ew;z=?wZ@1(?xqtw@d}nXk1%sD zbu7MlNf=J*B_7i-F6bR@@O1dg$WEu(`N)oYkl;Da%Q;2fEe_SE$LDI4JHGtG^F=)0 zW)vavk)GQsbCPWFw|gP0&$@m+nj*kJ1uAE)^4Xjg!u& zvolG@1dlS$&#AFOL9DS64?_{@1`&XgNTUP2L7k(5|Ng{*6>adU6$H2Te0!Ul^|TS7lfy8a~&BiK05vM0X`XHaxb)FSTCJ!70 zR;-X11wNa?K8T;I02yq%ui`{=6Ugi;Z5^%n2|J4QH1NC6a9D;)e>$fBrQO0;mDMa; zfY?LsMiGTU;l)5FH52D5sP9H130BrW>HA=nBU@@Ax0S3&6RW6q{X!lL=@5@?(V>)?Qm> zjVta|!iJennNs0kq%+cIGlwU|P?{`YsD{4}?bt_~N>IncikKKBeH~D%DwgNZrZ~s& z4%r2ca-%wCw6#wI_W z?r%oFh{Op`37=l3mtOZb5?+X!T~|0UYQ)JtU~OK_YD$Ggv$RpXytRCT=vHWf2S6MHwe3NT<1@` zg#S>j(`XO=zMr5wE4tL!l3~C0bQ>8$uy+iUZTS5bpZby&h8AYiJl6*98WM%VeXeqd z%@agcPFTa7Pih&+>`1szdm%LC|E>FIog$xC)t=rC#+S#GY$UGw{$44z%q9b;mc=i~ zg#rmZ)5DfhbsiUEofD9%)<2|kmy2|bc!gv6vm~+WlH5@-Fd`nYrTczqQ)lYCaV z!CWJvRON;vW^WV?aLs>e<7ynJeF43bocQpqmM4?H#D>TCfo0=1qY6WgVKMy1%Z?Dx zA!VFw;SPp5V=cV-nO#jA__e5UWGjo)(~I&F4X3&(~}u zvj-?ko|gx%OP!2FjqChO*YHq7c0vrS*l4F6)l&)DZMzza8X?Xo8-LunZ{GXHIwHV* z4}CgAyT}5Wy|YBf*RgLNNprKu9~1cfq4mcK)3V!=5OMd>@K&C~nr-Uj2rB_JN+GHb z*?WWh_!eIW9FrD<)x(nImv!v{4;!poyl^nZ49%HpNPZmu5D?fcvfC zOoPqScovgU)WehICIPEIM5-cA=(NxATx1P}dllmDeQ{bhSz#S%S^EYISE#D6R;oJ4 zwx)qaWuWjGTl09EfqA-J2cLl5+5}LQv>kz)P?MB8xH z_J11*!dJooV1GX!H8FZTxi~KC7C|uP(6UgZFp(l`8BTg>AoW4sBd|l`yCBr(;`f|w z=TktvO#Uvd{8!uZ)wS3q97X;tZ!5=q#M0urclUcJ>62jV`>+n3a3P0gb&rFuHb98z zw`Owl`LM9NNpWe0L%03s$;eW!e0bFP1YccG^x0^n^J{Nj{+I7;+^f+CPwTBe^X`nC zUmoS3>@3iR^kVY8u5gG?>SVthH8eQXPkd38qkV}r$9k_p098J6P#u#cPNYj5D!+X5 zy+Dh!75-*ES}Bk|w~j%FYj;vvO2|~qd!aOr-7_SmmENV#NX}iPp;{KD_L?dWF$qzF z*f>av=4(D6exzv(Dk?=G9_R!VW%DB<*2HqGpB2cVen*r3pNH6c1~8V|o>5xkXvu_l|gbc4Dxq6g)HQ zd@J`t{#<;*D9wNSdyMa_j{p)Npu7gh!r?vL1 zgCy(vaF(%1hp4p2#Q1sxXdz&AHmy?~?d4tw2oW-U??`{C0Y3k{45)6CIKDU#QhlUJ zDW^;F9M_oq&>&R+UMRXxr~8bc@Rf5iZR9M$reTcNX7KTsfnx00%VBko{~17&qfL2U zmDP)NH`Yz_&^NLhz6uroai7|rXVA&KIXv&e(knX~FGx%-STop6Dd+Lnq@*P;w^WoA z&C)>vWY-*wQib-S^(r%zYT*d7CuKl&Z};Roa>RlXoC1numO z!L|($J#TzH=@DgYbf|l+2DT>!gtW9wpSgdn{&okNM@u0n24Sq75dP=f^8EJvditF| z^+!cUA9algKyI~is_d_wW@jf^vCIuigyPu*dx4em?n0bZS>LT-lUhxR@6+p#OhKf) zwlx+wBBl%0X!0sTGo_ikv)nEc5~Oq=wF~=D#-?H5LV0J!GU;*v2HeP#bI)ZmTc0xqH!Yw2 z?u^3QWj~6ss-AWD2F3SyVGlaKs@QU3vEnvT={9-}y4_Y)3=x{b^G{iL**~w1KL##TFQR&$YS~bu zJI?axkpZ7|JRSQ}maq?Ie{YENenZv;JQ|sg0coRY>_c&oAuM;udRHHYA>*s1J)Re6 zKe;T|1ixYTf7-_4KewMZ=hA%rc@yPBpzTNgiW+)}DPh!h$fn#2&B+?O6W}KbPZU9) zOvq1^|0y_C0YUyRg2wVQa?QKXsVO7Ojk>Sa_ck2XCK#*Gg`P9CI{hB{W;qf#f4#`6 zHEP)FLDPKHv-|AM;Vap{_WY-R`zYA@2J!K1dM{vE7!iY)Hr*oBvt>9_@Vb)GC>p1H zDLU&^P1CQuZi$H>a_I9E>o)GTw7|Wg6nR+SI{z__#rIH*0rEw7sNT}N+UGL;hV(vG zZ?QGCvL14LJ02nv`2O=Nza@S=h3-cK9;VH?CcSTK3jF*}^fRNi(kn@v)6{R|oe~fn zW}?j2R6P(pVBSDCsa3`T+9F^t@Tqjvuzm?=H6b0=J?+Z@QJd5=-E=rE3%s(Pb#Y$d z5OgPj6CJy^RQXVCP8J`AN>QLk_#K;yEyYwW6W^iO`&o%_cJAR&dGV*A>9}?BoSJ@` zm4r%xz>*CQO^s>nwOo0#^YO(iHoLUvhqcBn6#5&93CnsChgWdzp|sc3lFNkg8z--f zB>n z`9qs8qsn^^1aRPB8~rPnK@a%`-X}LnV4kmEwdfltZEIy-7{jvf0G{qG++h`Vnp!oX zMoCTEJ)pP8xX^MNDXp%(w{}}=|J{$r`Qf?MW#LVuuk{BWn#f8i z$tj`rJ1L&`2POG&kvbU^#4O~zuV0ex167WWTd9b5&das5eQq^`P75>_^@x|P$1TaE z{>zb*Oyz1^iV;Xu>v4{pk&LAxoc3UPO2 zlwGW_$=+ux+-VoUsj=9NNYLjyAq>8^Uvrl7-rqoi37apw3y}Xura+xTB?E_hb8=8UKQJy1-v)P>~QEFi}t& zn1_58=zU6L^*dCIW09pn{MCGph((y{Be=FouXTXX37bb@3fFIR&cL6Pgf!?5hpp+= z!Gw`&{`gNiXN0`!qgd|6tqqvacyix73Iw^(EEpYKpVHCEGWG7;mJ-t`62FuM9*wb| zZ=(fny0gJgp$3Z^OH7u0joH*cJ9`j3=I|4>s8=bYahf2N{nmvoX$PR>H`=6cg2Kqd zbg6Wk?Ci+|1JDSYhvc|TP4t0@W{4M9`&6>CSOT{Bnh{W8tZo+G{bVV+VmD*6T&QV* zQpVv#;wP2i7OPhx*D%PX*PJkjeZU74u+*Eqj%DIg*a`C3J&#AYpKC*F+{pc@EG@+* z435t_2Un7_eER3C9^=h4P@=hG=d2s~ejdE~Va48H)9z*SAx(?9S7n&BCklR`C&Xo1 zo*Pw%h-+A0p}djZu2SmNCsB$ijqk3%^caaZKX>Z6Exfh8(|R3qVAFa~v_uuGxHZDs zi63X@GdT8s@7l_FQQG^;YgXJipph~?l)AaI{u|}_Q9(@156 zK-;o)+X`2z%s<4u^(O6yn?NI)h~W_nqC)O?%i%!W6AMCNhX8-+Ar6%AdtS-JFxIoy zrNDI`PCk{nj@vSO&o^cQZ)S)|pGBllM`(zfMm8?0$4>^b0i6m5O7Gd(r@KA!4Sfqn z$Wu5um?J7g_9OsS=W_^_$=FMp{YU(7QDgW)M6#?6Z<#$EEf?#x3HkgP{-U0U+2JZy z+xL5-#g-d9dk;t0=rNY6JSPIMRYRxo_t48`va>o_E-t zY}P4&i+eRH-}jbTxIldSWw+#d9d0$yev#+mBB^6RFG!pK^Ex8d^(A5PK*m;){f6cw zaYLEGsA>RfDA9U7EjZjOA)U@IxQ{gsBO*rH;)pvF-Akd~es3`%QrI-4^a&`WBGB?o z1W3#&X$aCNsXS0A)=-i%gEl2grLwtHSdNFmwj1QWA ztZ>TaZsKE5K!0&u@I3?RUH;N6cvj_%eT1; z4sE_=L%iF`%$&xmD;$abtP^T9@AqgNV1>hW(y3@RWtVsb=(c;3vh=dAYTnN#PLt<>Y64lvg=XvS;Rk1vH%^rhv%~> zE1u5Y_g*AKzn2hND-3LwT-)E=_Co#Q!`&@E!5zS%F`3qdzzdjj28LL#wrT%%G+2WX z+j)xgfu-BAq8bks{@(R2!jKXV2{v8{)@eu$)%99k=n_VDIJ&El_nzu{Lv$il&*jHf zw&1L6KcjA?tV^5-(ZH8`D;O;$K3A#_oY;uWRAGS!D|xp{coN%aFYXenhDcn_Q^Coa z?@hN3iHGe5VoCfBJXrr=pu(^SMPUW>hUP_;#w0jzjU(z*tYzp;-v-()f%W)npw4{) zy;hz@FD4z9|3b=3Xc5fKlZw7kxN*k`XHjEAXf-6orhM>Nka1adpA*QpK`K1(Aa*vm z`e=&4Y>IeHPZk&H#Ci-a8*@>MYT^9pnYom- zJa|9yV560c5)S1^fbWj8Zc2jHG+M7q_j-iDrRn1dvD-0^6W~7J>pafxZZZN3^kS4(3ae@E2ot9 zGp#~F#=oef){!~Q5g5e2aB4kM8tTMAv9pzva-?OFjjQn#nqE^WvyCy}FDMBFDAVqJ z6u>ES+K#n7M4TZYUFcOPlOlOpT9=Tv)2YTbZaE2cMle@FAU=G1$#F2U(RlJ-otSG# z0CcyM+Hj?UT4@{^JGn@ukCKz_nKGu55DGiqEt!vF_N{jr&qt^PaxXox7* z<$v|lkhNrO>_c?d_75eP_opzJxgr->M*Qb^+RwpnqxO_^N*I`E@I<*|a*<%RUFq>- zUa63S%4bkya1SlTq-*=T%xFzbzEvt-Qu5U&ru#4CIcPsWnbJ}Ku{0`mmu%hp-Os&` z2P#HBZF7HFm?Hu)H{9dEMq^xcmBVs%e*7v38%{oB;2p{4%RUmNvK>127QwTlmqb`fud1=2tyBMr^)I#gEib`HZ3}QHOt_ z?g8^~LFTl2x%V3*%^x`+Y8pm|2Llq5k}@%G#Y&ezRwynhJ}fOn^C2ZXfHIzi9)60V zHlN*dLEXC~>cm=AT=hT%rm*EwrpyNqD*tt2`(I^^u|$ExCkhgK(2dnTqAuzgKB!Wx ze>aPt*bYN!+NPS7G=r(QaEVO0Uddh_ldu}Tb3sbS{yISy!Yf}&BkarX60MJUitXaO zmxb)~uDUpDo_ow0Y$8Rov#S4U_aBfs9Ta$$hS1p7EPg8t6GgaBRFS!Ckgp@JQ_Luk zlj`DrfB!7vgXl`-0k9uAoe(6I1&yp@Io6Wx0b1X}db|`TX<{NHC1h@KeM6-|iW0H( zXm%PP5Ps z548Hj20_WlB~~tjR7DqQgej#&O?ZK?YAf&ooRo{iD%4yuXu&0`P%gv$Yi0%~6*Wpu z%PyWS5J4WSdI|$#5~c!to?cEOT$nV8tq~IWb}4OjMwjHrZy%xEG5_kKe!?Jkk#ed% zQ|<^67JCp}kk9sD8L7ycVoPnmjZwn?6uecRnMA)ofki6*G~1X54vI;{I2<|(+gJQ@ zF@>ef=Xp4**^xSR`tL89JW%uz-kqjOD9Z{bp<9DCvaY~F~! zW#$Z{!3iUbh>ENVX^d!&aLsxMKLWbhHHCS}FPM^Iy#h2(@sr_lvWJLcz~wDkA9p5M z^~U|{d*M_;VA%V;iAMYiifza|;E(*NDZyF-LE2RoCSXWgwj_eHbfSbp&9YeEt3Gm` zQjvseQ?ei~f@HB-tqKDQasIp0{`)QkN=6Cq$P&kr3FcE?VueDmVw+7rV#*{Zz2Vt< z8JWla*0j&~N9m&UMemD^q_kRU_HeghYub20`t+e!i|TL2)o1*7CD6~q$CyuBf|NO~%BK%kQ1 z7qxWh&q}G(`kc}S`S8tDa7cQ`hUBCwixY`Y39){~146$4etbaGO3kW8lvh}QbM=F_ zhbIdGC&3Y^$g+hfvxKncDD8&Oq)L2OsuoOOpwboP;-(AydS8s`eVtf+Em@O~bk4?NQKlpJ}yo>t_# z?~OkOCf$LtB>gR~#<{Z_Y#KkLF4+o>FwHq_q|I4xgxaMgIP72EE}hMj`#*mn*N?-aU-xb90GK*Hl)bvfX{!7w79Gem}j^5yRE2L2?cH6lHxe>>8)M~0e*PjS;aQEn=%|Is4UM= z%&TDENX(UzL}DwMe3szDfvzlD3hyuZ>G)@B)oNQM0LcKahmE@JAKmcqj4!8ySb zNMGLR?klBXmM+37>`1`LaImM4v_0a!F}JB)^7}flU~wjJ60#NdV*zqqH(t@Jk>$J- z%Vq08UDeE>`7Mz>-QZJdmhb7^McP+x2YbbMuNi#%1t-at{x7Hc_ANUae9wJ>%@2Z` z=VB)TPrpY~E$V-YVF?){y>hW!XzUmF>b)hIkK?$WC$(@%8!J^)^j0F>x2p1nu_d_g8zMN)?-m5bwsmKTPH|gh9Q{ zn9Y?pMFYny|K%C>Hu0KwgZySoK=MU-zpUchfl;vHjOL z;h~Pd>MnmqZJ|2-q(gl(kZQb4ORj~+NZk1prJ0Sx@o`*7VEc@6>+9ekdbs>n_Ox0} z*Lr*XF@SF`0zEnlfc~&-9V{H#99#Qp8*Z3b2?hko1AqY7oYeoScJ;P9Bu~(y5&g6+ zNtsV5Ay8zA9PYVI&baz-U;rp~JJC(%9TLPZ1J2PJA_ayo&>GISbG)xM=%$J){5NG9 zbYO%Ir^Ip?4TVHSmMEp7?v+>a&GH~zf#UXMv$;D7-mt+_#qJSUDDPk8g|JvGw~&vv zCeA69PP~3E)Jne;5@2YP8gK&rh~B+ zotBEOPn(hBpX}+S%yJxmPPN>*~n?`pTdkf*K;653k-?^h`2mmcY_ID0~F|tAaS5CFCU?Ez-m3qL?XZzc)8?ckzRN zT|qx$!+gxJ=Mi{WEGSp_hR}Sp39o=Orud-rZ!Z8S7-LJR&8((XyGs40(X`Ex`3-3z zVx`Il0qwoF|15J3v4W^FWhHh=`#mBcfosDT_)?~fmejF2lo+&y=(N237(ByC`Q@(@ z^X1FDmk@#O=7o*4vDcxdmeI{wJPDrvTR4TU4`1Hbg|DakXIhCC!1m6#$G!4D?YOm{ z*_H**ATr|9CXW+6+~pd@SzSUb_?ATj5)usF9f1y4a^nmF@z&2v5$(YW%h{9EM}z^D{Lgo^!E;~aKdY;1HQevzbme|>OHl~@g*b#8aDBbt^w`sWIc^(c zS%jnA08Hmw)t%rO9sw!)Qb~^AC;3qGWo$EjEuvO4wIe_zZvg}9cQ-H(Eg7C|BQuCXT z+aDKWtonP>(k$C^Flq$}rowJBiYs=T_B&n zAoN!0n7$E*VWrx-zE~>00dk*ed{o)=3(VSi$Kk904&kmTdZ8ivSHI=umx}tD`;stB3nbTXgS|&TUagu^V45Iy0k+Qjn)e>YCt0|OGh%Gy%jlkfqZ|r7D_g- zHZ;k3D@WL#AkaxQhlQ|DFK1Zul zCOs@-BuoaSei$pO{-QPRS+23Cxh{$Nwt^@T5r3JxIU&|d?W#pL)IF5rS*B4^2Ks#t zkK{w)9H@d?c7G~|<5o=I8RJ07J-8Su!TF^N5)^JXydz<*0Hr87A3uW1&hZdVnb79` z>;3#2bzxqK>h|TTuJhu%5T0_Fmbzd3zZVZmP{%Vw^E~F*hgPvC}VYu_8{Y-%=J_5=QJ-)><5N z`jJ*5^@V9gi*P5^*lV7QJ)!Z34Zc(SSNjE)(;uCXjc}sGIg6ESuUe^-=4%>rMkTik z6JEqjtOBZxPj!igE}H(xSeq%xogX4ry&s7+wKUlhcS~K(YUh#{cS=vxl>{ii;P`^o zHe6a3&7$?1Sh9|5Bv2V-I%i9sX)^X&5AJMfuER;a8fu9ZK7=Hbi7E-7HwT~gF*luH zMg)r#rpxnH(qFs0@L9es%nBu&pm4&!9U^TnC}yBHZ?jqCc3(O6(91hThcApH2G=Ah z@m7uATHu(wsnqs7{9d|Qxvp6`ZD+Lf+xA1#ke_ip)i!WzT*XydWeg=PJY*5Lt@HQu zA!K((99frVsV}d7n{7+rd>;10`gi4#h-Hp#pR@rf!kU5kuw8?xkHB69=f$w)pzm(L z1Sq)tDHY2&+~lV=3L(j_-yXQgj{5PIEoyofXQCH`F!YIvLiw=<4gKW3l z)v`wM=JZNTy${HiOj7m=Rf(~2P9ibx!B%h| z8#^ZT%hmf4TTx^c3%hk}(N*h0d&?i#TGSQ#TfkVK&}Fo|^?j!vLo2-x3LBZgAo zhs*K;6T9wj+!*l=P{~hCrSdO-Yd7S*`@(=lytFrB4PgrsdHi{ago(0Wd$^N0MABs6 zw8kF)Dj~L2ft=BO)!aeAek$z^0)a1Qq&Y|DO12Lkbi)A&_{K4jLfsRl1l<2}?7fb{lYk6{L<->g6a zf!~HT@_BY!X`Adk|24|N3r$zcPlKa{R)_f73`d+YMY?aVDp~Nss#7WcWc9MOhQj7C z%e~RIx-Ab;EQI0%W%a>LhkN9G`@}qUM36X;q2^Bw^Yc5c)&Ah#7aRc~*_Epu?#0mY z!gyHl19eW9WsXnh`Yo=`xv=2j;uexy^=vETv!={y(+W8{L0^=AcnxN#Xr}%QPQM!2*-(C?fva?kE6YF^zmBPwC|Sgbd}JdlY8aJ;_p(S zQ~(BO1#`WSQ~Tjl`i8Rrot^w(XFiIVH+~oRc!?B*$gegfE0W-GP$D?IuA1pw5{GE~ z+$0XM%JPTdtYe=zO`Sj_ueGEKm6?hW3)m=P==8%njyXapcSQnT=Pu%pg*L}@CwfT_Ft+(H$jo5tx zsTbrsd2&3p~AgX3036c8^h3bFrz+lH(y(0s7awhaf5i#x8u`Z+yvq0 zn|ff7!d&Jk5(W;aRjiooe8kJ#$E#oc_FDu=-&1YFkJDi>__N&tWsZrdo4Q+Xs0)DG z>}lK`c^oa0Ykv+j9n&VS3&hd%g{897;{VQ zz5vN!vu7V}Pu3WIJU!E$_Q}v>uE?ir=)Jh9IowLK%o*b>IVECKd?R2(X^#sIo!21i z@K|<3GbglUSZ|(*V$@hN8|0xJu6_4%`dy>L=)yK!)BNCv;xAge<8j0lY4Qy6K=ln5 zIWk{+Z8gF*>JInSRkB<8kq|Ty8=#(6)qsY$3LXNBESiPH%wtuE19Pry?>Wi8shs2@ zOp`B+ibROSh_n)4!R>D4o1b#v2m&n%ayeYJyr$4Uj_k%5QI0Ole6;YX+`Di#5`TPW zoJ|pNw`~!bXgNMk!%sx7n$d&EzetUzzy`;`QWZyVY$JlFr0ITt@$*M0kk_@p5>pEx zc<08Xo*zSEfv}aPvp>{~P2h~Up*n%4y$XG+Yq!+widZCwq1c3X-siF|JGAmK!|qGQ zxP%sPh|m{@B77J2^WICuh!;68Jl|IBEqp4=ldwA|`l^;9#%3UVHG0RDEJve@XW&|E z=r>@Y8cu!uB}LmwNxx#X23&n9pf(YHm!-Y3_g&jW!I^pdvH=mS|-nXV+BFbyj8FuZ7*K5_r z$ZX}2Z?HNjP+l_*==6=K^(<;4Z=pQk`j23ldS_0cgNL}eN)jvezDA;i`QdLaN*oq6l zD9X*$MTOluG1vtVIWCg7@RIiTn66MU+TUf$8iFu;8pikKJL`i@R}_O)E|k^sP)$i`U_%rf`&><^H(A# zhRx!c0x#D|oLrmlpi8C--PcPr>7XPb=KL;* z<708SP6o$bEbaPrBYs;j3ZSgg+jNPpaXuTbpNPH9ULM=MHwyYDw6l$6AARPBWOPN$ z^R(<3I}BR$ykvG?5TZ0sv#<5L{lz#)vU$45Slvl5++A>~#Vvd{)d#-KZrgiE+PEec zb(Vt?7p?mqcj|41EcPG*d5S)Fcth15dTI?esucpFH2-rIw;>H0y%)Ol-+erT-9(`4 z<1QttOQaNq+kmTkP)P+|DvUyjjEBT<+XU7-xPuWqdhm2nEC=Wz%#JCE;zC<>^eH}1 ztv8l~jHvJdzc?=XwIe&-s-$~h9(@yzTu5&=%PMkcv|Zw+G0(K7@qKFNgN*hI5YZly|FNEH`rlZsR|Mz!MPcWx7l_noX#J z6-z3eTYvuE(Kbq9OuFwHpS3%aMIdnfhHvEB)8f_tg+{-DQXxBXmGbp(MpjZYeY;NG zPtN01DF`z~A*>OM8HHn1ZXZ6Qn+9igqM{4q!2p2{QZOjbx_K@D-mzUt7x;mZx+3_k z|Fy~rVE5h30N49I2}9>t%rY~@GT0N>GKY^&dTMI;gZ%<}Jk+>a%TW~LtXYrXxhe|+ zuSqHnGQ3y{D?fy2rDY*mmg^e*eY-nnU5)16&&v|@jGwkcsKgha_}bEU6UW_)HSVW} za%7^x_P6v-T4dCb-xCq5p--#1&>2r#l9UC1xi33~m+IMs&DE1D#YEuoJN929Y|Jre zok!YHtb%>C1!yE%i(p*%tGFh&z5BVfbyFv@D8wWPbG(iW(-V5`#3Y{iZ=KTCR*|)> zH0smvoUbC9`W??}Z~L9r?jWnt;FukV#>nGCsPPQZAv_H5T-!TeiFwd&J!OAAl5SSc z@h~i1&;4Tw$BFKo`s}_8>_QY?SxKy84%@1codjuIZ({Y6DlkG+;vnmt&1}uiZ}Q*6 z!231Re-e$sv^e5M!}D%+lF7tQR*4Bl?TXot&y6R!hJQ{T0za9P(vJny5tZeqLUO{p zqc-|;pKn)bjnKIiI-?PkJ=Mtr?HB?eLOZV1hvXlkMgsd#xn(dhs1K+rb#kmhUGHF8 z6~bt0ib8ud!8=F27+QmaYk($xaw)gNQ4QbuYRIDmrMU}oa?c1}PQF25B+}AtIlJ>r zAGhYk1Or!`_4c1Iiq~=;t6nAT^Rjk~bPciZ9~915hqEE#^03gbbebuZC8K8m;XqWYtMfkz$DQx6EET!PrZ`&E!lxcnDz@$HL^JDz%dq- z`S>aG-G}C%S}alRaDM5uOw++ja{Z0NZ|tx6+)t8)6)(bb>Zg`>z`_6}VReFY?FOhKbrrmuhh22Y1=QsbtL;*r?rJ|Q|9mda9dvsPNU zh4z;Zn(&oO_OqR7-_37ADtq!Ju>JzWJRGT~=&wg3L;%E8^YpAKj5-(kZtV>M_WI5S z{4-J&E_9)7K}o%b)k~l+zFj#r%DMaI@2d104GcrTLKM8Uliy2^0ihNEgSwKnHr#tpE-}T zGPoce(r+Ou6Yqmm2ftz@7){Zt)Y}2QBqE%QjPM`$`)cJhpovtZRak}rsI$MLrJHTR zcqBLI2t20brK~>7b5SwTd~qRv8LW2^s-+ydfXGfTLtAMXYLB^>Pf?Cny&HoF%c zXB7&MPW3mN3gbHa6<&}gn*x~h3|puCgXhJ>k9_AMH%~=%v%+Ae3`}h*TUvoU#T8QT zyXgjeZAy!r&JsiuKHdZwYo=`1{G`J#mfWgt`VTriJ?bvtVwuG+d9-U~DDn{&AbN=BS~F`3_bR^>kYTZ*gBN~1GH>-C*3-+&Re8@19wI& z^A9TsJvA6wdbOK~?P{h{VsbWa$L)$I&vCy>OS0wWE9Fz|of*|H2pEtOY&ve2IS13} z=c@SO)vKIg%pyd_Q zAA{t^=s2ku1KOTjjm+&+v!OWmdqzz$8T8 zzeX&LYb>CUr*=%UG6El@Bn$oVRcoO#A+)LxeF#=dK4FoO*D}UIp&v*ZEu(NzS|hyb z(-&``sIH@Q%ttZmzoO^JA_%GBdJMjtp!X?d?&V2d#x@&ryxMo@dL~hhB4=)F45X}1 z`*tuSs1FBTtV^`QUr7pYCzwe@anIE&eIu!dsa{hX>o8}gK2 z)a#=v(qCXPm3{uq{({74EH^@%LBpy9-vGJ=otgwg0DP%O|JI~T0ERnaPm>XEo8uPoy=2pRb%_Wkr ztFS4b+4ZAOe6c*ngeQ4cy5%OecB4nsge&=}mDnN8;geUxR{Zq(8fwT_^LGimDT49@0m`nHEiDIk1Dh61=Sb8N zijM+Av}^We;bM%L&zm_4O)LEKXbNoy|`7dnK;*8 z%(SN*V)A1y{L0c+H4}OJsz}4L)gYtJgoB>xqem6rC9UznhjN{qL8Hm2LTI!_kxw8- zuyc(|Nu~WdD8%{n9ekP{a?TJ*fFRtP(*-8_o4)w_q^q6Y$F^i99B>L-&_w4X(luq= zutdBdL05q1WZcVHL3?CCo^a9OsIHS5a=jY31S~}8=+UyXtXK*1K88F0GAhnoQjzfV z68^+<1@CBpMgM{Fm=sq9C*^2QP@iSFR-nrhyK`q5U_KI&KyhYc1*dhAD4h=@Zlg)-xnStr=0ZQN01d(6vU!{P1qHGx zp2`)Cwt0}jUcaD@Nzj|4(eiLjFB>@~se^h-K8wnkY1VjrZ zwh>r&CAUKg$XIpPb{g|Fmw@sTk_F`}`q{Gi-a##HrIv%Gdg&1yoBr=)!w1i3g4oaK z6&aQG%ff?l5w4HYdHAGDw@VcE%gIEX=JvC#ZYQoFGh*R~JFFFvgtkmU?gDvtH*NE~ zOi^$>F-wZ;o0&-w#Yc~~RL-q#oY$H;_ea0`ek?dsm8U+psmGx#{Np7eASV+h9}`gd z+HIOEjp!kY7*BTcD*H8gk&*$7+aV=Mh<`(Ld_uq#daMLJpj%5d)DhQL#pqE5v_`|A z2u#-<1!yJpRULlMtLZ$Y+~N29u-7Oyd|h+oP$`0bsHDUtfTlifTI zdMQJ^I&O)uXyJlpZYZ4oNKK-giB9sRmTn&QK(j^zV9~nO`|4_M5 z4i{}cu%RtJ3d&Q`P>JjC z7^_=iL8%>@tf?%*mtF##&sMXdjxkP|8_68v0QNwT;xF^1C;(fkeSeq71Lh)BgMYPjB(OImM z5+q>>RaYjnkJxa5grM!vytCWm1*YOhX{zy3Lhvc~RsJm1p#;K8e& zi*0qKF-j}m*2l$ycqRh4P+lQ=Q0B5N^8MEl9`eBql7Ut9A+#TTpW}xKmw8Md4IbW6 z#43kStc3xU7-hAxIsFIY96*u4V!K};^2DahYD-%^jr0%wL?wKLgvkqQe&~G{FVYeo zM3f=VVU%7)xhpv!;kkn4DdccJJm3xy2GU@8kLE-JQoM?ef=|jv%zJH4z~#M-fs%kN zhUSy}mWdu(E!%z6EQL#Ew3WSamdCbPrifNxCM95Wbgat=`2ZFtl?h5&_}TSSuPouV zf?G*Ujhm%S80{7zc#X^mE}U7(uxVAesxYx*!N{jzLWaPXspD$K>$Qc*or$aLm)-2d zTU`R=CW!wt1t^~#PhK~&0l4vRHY%@mY;_#cc?MV45d%_#Pg~F#lTKYaWEqKm;t!~E# zJFfW#1(wAKuW9C2p*#PWz8s(FiG6v(2A83S%Zay_iqE)54ji}am9%RWNSK_dU7Yx+ z%e0BNl7`zWRg@+<@-O7h!s1dh+*fdY4F3FRV0J!0*+X6zKO5RyNA-=3e5c-fAVqsc z#Pkg|6uDkf(!E&;=;`!<&|wrQBKoaThFsV!EL9gU*z*5sjo&W6zKm5N@0Ie zl(jg(EYUA&&Gz_@X>8C1M(Gptw+YAw(p{n?F<$#?aUtJVz9F7(5_vbdtWt%apzo6d z$l?_xg;Szjy1&8`906&kC0&v?npI!E%Fj%xh9qLZQi3CSR@sgjW+y~bW?*IRij1I3 zs=LWq^`WTek-n=I0%1H!YD3(Kc(ua8vx6Aib#BxFBeZeDtWI)mV;mFNjYo8#^M z32H#K1sb5H%W=ml?3SFuv)zBa!P{9L^U~vEPU3_rDK2?2;N0Y2@h)dZB1!w6D~v2A zAJ6-+NEA7MKpIYTb;@frT(*(dRrvhyJ6v>lRbyP9Ps%3bdSNW%r?^o>;X#Aj5Rt6J z&`@7(Wsyw!*k>b*IMWyY2UF?^Sz01OTA2!TIc5LN{mUcvv4eG9ciwsBm13GTuZ+s$ zGSh|aR%baih~J|of(_G~L7zK}p>6wcQ=~-sRq|Jz6xIF0*(T;x3KvpZ zLk#NkIW?rNk{__FQ~4f?MtqerqotxkN4@Weg}Ea;pCdM$ccl&pkTKwAN|fHATD9t4 z4}3k!i!QBXf+$UC1ZwR=>?*zjTCMT~y^5_N?+0w5^2K~9q({Jz=lDk8Bx<236{Ye9 z{s$>XNl=X&72~DFCefLsi%pSjs{O^+J*>IAE6xc9n@{W3A%xo?qbx7 zahL?9)@r7pDJ>IMe|p+Kcxs5DR2)6mE&t#2PxO)!10 z&ur`Guhk|74MzNnZTTbY+jf##eQ~3Rby3pXKi!R&)LFw%8KquKw4Dwtiu5DPD-VXm z+)w7tBDQ|SF<^Kkb~O35Cty!GIpVpn=BR$sRw2brJ7n?r!;MeA)!2VP^X9A|9>;YI zq@6jM+Ud{)B*^0>x0%pZyZ-&!TT1~l{ZYI^!5-Hw(%xA1xQYN?v~v=LM<&cHiFl6S|0e;3j+ZXiT z;+kJzAAg^ARm^G;Z`t6BSg07uHx4O~7W?D2t^u9Yf7Dko14zvtL@ z+FryrKmG91qUhPJvb}CvzFF=}cRoYx;a=Lh?6JY=wL39b?M4AxkfY?;2|B)@2~LBL z8x29UOGbVVI}K~zkM_SiCjG&FlO$kWOst(&Hoi8y{S-bSj_@$b?}mrK4AWHfihRMy zLn6D+zR&4=AaoLrHa+XDmWT|QlKYY$>+c_p@;auq)-lt4kI!{^I7xc&G7mTcFIlH> zW}ISoWTXKXk;FXfbSvh!hOxICYbXlAIo`Wzo}r!u20G@Y@yS?acO>hau{Bb^HyjN- zhnNMnWoFW23VfPa$U7I9y;dUP1b3Sq34Z!G6(KqW-92^U4u3dP(r(9)FPHz=rmEzMh7 zpIEh1I#OP1&}gHLwcbT4HHVqvLYAH)Yp!GVPDN=368xQn!;G6H=MTP*$M}TT+v@B} zgr}+qv>;-a?N>NLZhx=SR*67htS*i89kM~ZjhxZW!0QP9iGc2pb0$5b_4ENBI6=w;-}5J9s7TMnv=sll@TgE{Fs= z1!Z*mK*E@1G$z$QVtE9~b7?&8b=G@oyEoI4Wj}*%mwYA(zHpUGJB!QD-M>~OGiC8t zfjoOG3S((WkSTmcT?{a8yPFw{LJhAJz(Y1-@@So2JngJkGHhLuW303yS{S>5lOgs0 zSv=!YErc{kZVD2Vej{Ji{y;$J^c*`F75lwU?ZWT&prE);4a^8!FD_x7$Xb9+FIK)^ z*_}fcpj@AZ>V~#CB7x@RBc)hKB{0ta#j|WD|A%CE6Stt&ysIf*WEah-<{RaO5`tqdfR2YL(Xx^f? ze|k`XO}7XNp@;`;nrI2~w;%aKl<0HX;uGiE%W}%fqV+V+x7X#2bumY~bwUuqn%XKI zs7dt4jP{dlzM#0U`_ZDIp(m@$8+3o$W?&@(%ZpAN0sqzgJfNkmj~C`+-b_ zjs0$ZF;p>MuQ;IEk*3uwh__@XVOd^-wX&(lgK6ahKn&8DzuwZpUE^h!(9Lr=L$fjU!Jr zz*hdrP&~EjpxE+Z<01^I-#7wVLCx4k3=h@Z-}QM z`M=Jq9^IBIEhQ$rvkZdwuB!$*H%_3q2D`sV5$9x7gBvyHF#kn#_*OoYZ0`$n=DM^m z(gRQBokWnH26=LrDk6TWD#u~4SYbr2skl?G(@-cc;&Hu=mxaGweEX&Q3WK2GSJ5YQ zq|YT2cByg*1b}!T%c+&vV?0r9QXXNhd=ZF)2o@uXRa>De1iE85?^YdwN$D(mWt`V= z8k3lWU}}Z~zG)WtS6P(s5cHbgp1oIj=>8-~$u7){BNdY?nrUY6Q^$e#O6%5FO*@RP zb#R6Q6Yt0tH(99_L;X01d`x`bXl{jQJ2J7eQ=F@s0OKp2qFh^L*ip-`A%c%Ny6)|q)jJP+wmw4{kVH}-yKm=Y@#hH<8{OQR|i#iL7hE|jCD6RIO zzntIseQ54?|M%)yW?o>}UoO%w9ucnOFju;o#Ej7=?Z>55H&slHCD2)7ixi&}FJxY4L$Fx`b*Yy^t;o?4q#D z*6opG8tJ6Rnv?C%FY4p(^(JRe$Dn1qC9`Cu@=6L6Fa&I_n*95C;y_^osF*3h{IvT)sB;;q zxg{Qy>oQ|6CJWG6x|_p_?>{!&S*~&r z(qdV~kSPhGCC*ua$|Q!)f$KygX})FoT{Ez|^pW-N&_z*ZN)0w%WY^!<0~^rA6xka+W!sUmE-*v&rx7^LvGdWw zAo-s!m5kD8&L!SHSpr_%A{p%qcM~RWdK-NAFge4z8~c@eNlKErsWt>+C$WNNx*kt_ zGW_C}>?_gN%5}PT(qppl4c04S(V0kk ztw+H2(SwbkvD&%M_9t7v9Xi>;7ZaSfKhL-MBTAWSbqG6ue*PO-!G0gOhPTm#A+H^o zcdp#rubB{49+(s8p+XG8_2|}R15u=)(g%|e!L(W%5q(yB=zW(^EG_^pk|uOVZv+{r z>aPdDs>J1nb0UA?u!0R+kMWivXe@yiRfd+8#dW1Kk4~;5$+Iyi8~XeNLTinYYV|gV zCvj_Na{oe;yZ+};NHn+`EabB@@R8rSJMTw8mme0&)7a{ z@Y@J(XHYgJs4pf47G)d;VXjlD%saA0(b=v8E>kQdlHPTZaSsVm>Ih4lP#dapjtoQ2 zxd2VNXetUHhjC!THs{pWU|VxV8?VCJW+R=`ekj4SteJ)#lz>52hxe>JhP7(71mM{K zeP{wJ7O1J1d6Wb@`SH!Lk_3JTA*7YFz^m;7~Kg9IO+VF-;6rKG^pdQ zkyVLVoZ~D82P6oM8q81v1MmP0ajz!{3jx2WB%#r(As#$?sUjer6_&mKm1O2Hoswf| zMvBB?O?AffI=9Ld5J{E5Vv;{uL?~&jpRS=Ca19JND%ew80`mTX(>ZCNl!cxvPze3) zf2Ln zlZqy+#Lchr(0Ha2MV6;hk1HTkAFxc9eaEm={*_;=5+qt4h6gr}=bLGDJ2*-JzSY3L z-x44$5B4=`IehO=5o#ebi_#Ez^l(ulaRH0#ylnnNrnx@ODedQeTOe5boHUOCk*82P ziiAf4uTGUq_+HhoCwZa>Mgrur%JsZo&9fsl>c|-ZD>wrYA#J>Qv={!{OF#QB!w{47vFL<c4nAeXjxgXGkp&_s>L^rcO}XEunDLN&?*6HS5xHGHwm%Vh1TP5M;8m@eP{4y z-up?d=SmAVqUl1*x;Xp^+5eMC{SU{Rn@Fi5JL8M>Ddbo-ft5mtOmZDLS#45^7|*LQ zv6s>+8p}e`dwU*sQd9us8|jlE#EZBL7#EZ(T`yK^NxEy4gsQ$3H};$9*-9W3t>bTS zx`h7|E-NSyQ^$QpPIx>j;y%~K1W5@BtNG`k`MQ`jj?x%TH!{d8l2 zn7X9Sxe*V!(d4UBu@GqI{|9+1N-3SI%lX4v(mLTu0zd5zxf$)8bW!(;EM5++m zI&OYHf{T1d+%W>4I+2?|p|F`j!^{`UhC zHJ2hou$)N%67YyOMb_Wd3BI3B4V);Fw2KE~3hC)3s5rv-uVkhvtFh~-ucb+Z4C|^p z^XM(3`IquZ!KHny*HX{gB-1z=|G!5BECK8+NcdxV)ssZpOLKNc9s1v7=X@SO2hBrpfj zMwO+MCTM?U8rV=YaZWxX`RppuU~O`k1SKWV3h%cQ-yWE!6pGAK_hK!bTF{R1T0_+) z7a}p?`_eUXv*7t@=pRzSzm`?;o7W`n7b_3dY$%T}9{#rPYN>ZU>OShRU3bQbLQ9Cc z_$AUH$Mc`V7jBY%v+CpS7U`xet!_qC>`%$371&fLWF!&i>DQb}ZJp~JtxLHkohy$b z0t~^bd+35CE;y?AGhIeVptmveFzfil-v~)U7mcHgx$EJ|G?33=SS_j$K9gB~c=ur0 zrY>QmcsYHWbX0EanqhkNzZa-VIlSm%y9iH(4x}xTxjvy7t?{ro7KjcN6(UOr<-~9Z z)CNMN(dI76mDQJLkS1QCio5M6m7wL*e~4FCs}MD1q_MA4wf;&LPS(&}WuHzf<{`o{Pf^%<>%GPxsvAf# zk!L?)AvQ@`#zOy@E_}7b$sjtsTbxp&vj;&x^B3uu52dmHV|WTT(-`tQm1FtTwMqGZ zzn}j(iBy7Kv0LM&&)Q9Ujqj$%(o1$Sbve$$VwHS!CHu&N1oQpwe5ydCv z1&(xUiztG%yE3iJ#TC+V7B`chnzK8+u=E`eiqU6{Q3YP+WTq4#iF3X%=-hb7{Gf_B z<(OWBDVr1@9?N)XydI~!{2uX-)Rwd^5RaN;a>|iuHS8fQSSQ_Q<>70B&pVG%hd2DV+ zmTyg=UG9J}OajJ|LWizgn3(PQAaJ^;#O_OebtgIDiY!R^x>N$jK=*}^LadJ=-7did zK7C&QUE{G<)&Q-mfoT|BpnLo84tD;wCA|(#{z=LXfRiMn6+t9U=czJ>tg}djYDM4J!xcA_XHAkGMOG(xl&ApsEr!8>?vRLe)2BZw z&(98AuLGwW(Cp`mQeCI9rt^gpSQL2PzJzJ0%Z0o!10Eb^Z+2`_yaDb%o;dfzaZuj4o*S8x@gsr zqEyi+aS$HGJk+2Wz=hV%0U$uS5BCYg%fn+`cGFEv5L0?=9)jd2lDFIR5)`l~`kDXx zB+9+PSa0I#YEd6~p;2BrWK>RA|4b56%+G}HBbB>#*ib|OBh6xQayh~ zBJBi`T{q!iVs658z$;N(lWQO+nP7rGC;=$Yp(&|wVZMr0 zBp!01qkMin`#ya+(eRowMM!OQF;{uHnh267-}j}97CtV@34`hzKG*+cLw$rl)6ZD@ z3HlI_RAPUfI+=qvm$SBfr7YB>Zb1&BD&>O*^k$aX2tP6Lly5%0qrF(Ibcwu zGUBXM34u~iyil3UEwI-H7e=L29*(L;jrhST9{1r&-Di041?rnSi|?3-MQ8&PWQ6aq z)?DI_)yD?QV%1dwKPSHDzYflCiRkN1?s;qLoHIfX9e|kt29$5%2FxGMC4*G{A?&@n zR;CDBMO9RA>S}p2z&URCDp+s=29cfu>T^4VNu*9*oG@~FkL(BNjF}?Ce&lyFkV5W@ zHFRjWa39CNwJZOr68|iwg3>9cOzy)S88^9_8UL^+VuFb2s|H=sQA3Fq#$2iQN_xTL zU+jP3AVa|v08(2G8^g;MtH6$CKG^@UCnAU7Xgvk|El2u~82`^R!KwjX0~WRF(+lgw zD-u)hGqiyxB$snS25s3!>=HQ%HF8QQaYemjiZ7vcu##EJuiqnM`c3z+UPGZ8%HW5# z+fB1{?W?mU#`%I2%GedRrR*YlLfo+a2Za7_7Ci%=4Wet(=~X%3%UtQ!OcTa8-DEzv z3?RZbTeMconi(XkUnx~Y;585{a11Rf0hS8YX%% z6p%V*CGndgX_0@A3`fbWKZFL6@shJz&auOzVEc_mTs4bH^31ry$_bb!qTF}m;{D6F zsPFTYIVr6bOh(E7I#$>UC~FSo7{dIMZwz8Pc4^eYi@B^zt8`M(daf4GuQ%>`Ssagn?W}@28~EuBXtl z{hWqU;ARFm8p&LGeUPy-C#z9iE-ri>R_2+s?@|v^UVY_mU4lXk;{#G&o?7^hn4p65 z#;==P3SjcNU%LKz1^eq`Q={W^e28#*H3Eb)Sh#%W)0N~^lC@3!CDl<<~ z*OEatDQY3{E|kR&=4kE#jpifMJ-6@qFVD-$n^|R%`LS5Pt8>~v_|_lM)ys|-2bMWY z09UC+Sp3i4&7Ylz&#%a_fH;_NnO4!xWK0Y$k(axISklS%%9o;*ANucucL8R-UTn|U zPFd(NSTU2g=VMl5%k#s^>CGXb!!pjoFRPMl7geI4%^j;ES{AQxI?!6@6O=_HVT7o0143C7;(qbXu137f#uwv|JMT{#F94K*T()E zgY{PZq;S&^38|*qB+g&8@LxX&;GimU(}2W2_8q*s9^~WXVwbwTGbDJxg%=S6a z)o7|B_-fM@YQJi#-gcWebTqhf3I@2Gv704xOMTyIsm*zx1tY$NH9trQN~c*fx9yMd zoBerC?KLvapjTlX>3Kh{U~Ko+s|7PV=c zYBJoBww|v-J!MrgHmR{ztOswOoHGCRTDaTh+yPn`vx4nlrOAQHMyJInrZgxw?-;NPifg^fbMPAjX9+AXti{zzzZW7lWSwbE zsq7q;bAKF$JC5@qZD`ao9>A3|oAbSWzP;%#D6kvYSy|$cXVzWotE}x3`jI^V*2}gw z?@RFeVf!(pm8vpo~frXr`G!H)AqnBq?ZNq~BdSM6SMEl1z2M^9tG zEl?d`&x-pm_AHd2->KSE%O0t!H1?64gv`fli(C*w={X*%r{{baCxa;eFaElLw=|4a zG=`N@N(y5y0hP|^sU#{?+(&+icx;wbJ~wWYzhxzSXLXE8^gPZr*Y1>m^brphZ@hi1 zlNg~FP@??s?Z&zUVqa{#{qU*A7}HA-c1FGN4V*m32phGUZ5C{It~YF2xE+!5I34VF z?(io9%~JH}1e>Nn+aik1?Ft^FvM#OtH*42H{nH0pf#tV_B!o26ESjFlc>;=9B+*Rp zNRj!1L@F?ybKhi8NF8AT{c4aid%Sh0*u9zDHPCS1@AA!?*WCqM=(!x2Z5ap%?1_+= zkklep6**v!Feu5LAp6ik_PXs=RzU-=yBbOlggm5wlM-C@sy0HepyxZK-Yc`bpna9b z!|7h{>s+Jjt%b|q)(aj77648S;#_VwI3Xdxh(nANoGDkmkR&s&mGv~W{!{p$PF zoC4brT8DXk&e(Kb4pP5S8s~$VPMcl8#A3mtLWMn4?(28*&iiacpv&?T*$&8yo>=D0 ztHtpuDCwYbQUnK&0a0V2w!`g9P}(`0$SUugq_pg&-~n4&v_0IeYlqqvRt-&Lxbr!Q~O5_N$7Cy{`S18rTMZ_ zTi;{hX5U48Jp20I;iCSc%^x4Ola~qtI7nQ+_uH?(?$t>I3qmy|gXfq40(S@!rRk{f z?Z*)nKJKd++Gtg%4eY?VXD4qCm~5W&4SsIZ6)SOny=8j~3!?$>P8>r3WGw8MmNW`8Kh1rg;J``J`p+1P z{ZVV@Q`U>y^k+x)06=vw${(*hY(#8y6E6?>Y~Hl?d1k!B@?Qvep8KMQBT3;hd=CLw zjOv+se)?AHyeL-41V;d@r8bvizcdH8Q;5>k+)kid`#-Z)D*YqhK-1k;a<5WqFWH+r z4kzC17q=(Ed}Y%tB888uUvWNoTGpZ`JqV}9b1kPI&$N1-qe?Xfrp%$-R%7D78^&vL zXllOcW}uk2)`FRae`DFgSW<$d`2 zGDQ4sb zc1SblB=9o#}z2R*|;|~nC@|p<^iB+oj;XV0fY(m zg2n4Bvhy{1JtOy{KlA5H6t}K0F7_Na$`@P~7^yPk%Gdw!=b#(d=_5MPd#Z-{^f0qL ze>ok_@ZNYH-d*lT%Tm{trq%Y>Kj($B_S28 z%$0yVj@BE&LWw!VIa%IQe$V=o+|U{8^HAqu?Re15Nn%JPvLgw22q}% zXY0eZKki7(68t^*YEu?}Sk!t6^q1lX^bM4x%)yr4L8u0KYWQ2zO~9L$M%L0Nrj<^* zl?8W{4PtjmzkXfM70>)iT0jU+PIsL2)W&u+(IW2gVOjc!I7^V_vpRWhJ&O^hnaw>P zP{$#OmyM@KjFO7rn_&W}i~PMoJt;8(Z3FlaVwkK^Qn zQZ@4}e~p9SsF4s32a?*(3XerNz(d<4F!b<9kt1&N;Wy9uJMZ!FFliyrM{iRwi3cVm zs56x=EF`H@KFRvTZsny?@ z0wi*aUWX#aPD5k{-p_Vx=PUh7NHMdF0O0;`-Iujh7R~I=!uX8NiJ{lVC$%~?OF#WQ z+wl~6Dz2?Q?D#j`F5W`K=YrY%f&?9b0Ty~_%mgsICQK<8k441v)L0_jXGL01w0QOU zWUy=xnArS2J3(7V{%E}gw;lRTzVR!D)C`a`kR&AB1GZ1;ZbPPxOtQOn{;zfiSfRKQ z!JK=TQt|c6syVMn;rHb*LF-bRVCwQnr`_sY@bkV@bA4CnqW9%p>cNC!P0efL)r7QOug^>H8w*SJJ<~`qI zDV&Kf^-RtW9z^@&<3!+;F*2xYKhuRHjr;05?wR!meveb^RpwS_1uQkpr0h47&=ZrA z?&P_AeCAC|a`(H##Quxlc|_p-@`hhOil1xYy-w$ENs4PUIPf~dj|F8jvK{1oSnAO= zs8>g4{~DIUAdOT=dxTGpvi4-OJ;k@;+XE(aRSiLsF(Dgt#-9D1o1uv@i+j)RHNn}G zYz{Vx+Cx;hyHU;y`m>Em8R{E{Tz0z>Vah43P5?y)DZFkZv+PVU^&HCz(sVnMx->Zr?M@T*XO~{<-X$AU39nbg-5qb=dy+iX&_I=t&Rz3qsk*Ecc7b{v z`nf{GwvOBmhEm@+j11Xkd~)^{;#LVBo=D;$V2C}fRVk&^3#{yjnG7Xz@NRu3Fn0UK zX$kg47@>&{uk5u2HT?5TqFm?+9JAAiwW~YTu2CIt#;tgwV_x(rb9cc)_(R2+J zC7UDn)*rL+*th05_ssoaxsB{APD6l+`j1}&&mXm5R1Jq}@;D5=mTl( zh*t1!ZPD#rje5VZgF(QQR#j*sxgTGY!>3GhWoMZ|CA_oc#*sqfGG-Uem1&G10?^=)1$U7E9~*%?h6S zE%M_-$fu(j%<7fsVkFBkC4-xfwfDtAw5eVw-l@C}cf8v3vnZlza6tA3x3ZLVL+{pS zarTo}?zz_?xbb!bHnDRpOTBvq@P;rpzWC?X{`U^Y@gqlZf*i;ZTIwd7AGqdW(+c*4z#V-5OaOm=q=Z)Yu7LYPI89Ty~ zGk56&&U4Xz($wrqJTJY&wc1v#g+z(g4Y-+@&ydC%n!k}=$B|wlW0fT8*KV>n+=pc^ zwN}?Jg{xwPURYeUS~bqUUpH7|%6JcaO0@pQ(e~(tR=;XSO(rflw>P25v05F+x0tvY z{)@k*@yGdk&7!Ra`P8nr_|wXv3B_B(THHS*IH~ZX#rr$4nkDatg#-f|sEOjhMds`K zoEKP9qq5ZJRU53Ju`04eZjepWJ-L{wYh0TB{LXcA*%iY3Xqh;6Uydx@C8CT$XZGge zJp8A0Ks@W&3c$^#081q2w*js7M%Gp<6-|KEbH2!QYvoY;Ao{gYykciNO0k2RD!;v> z^=>xs;nu+xo2b2QN*T%aJzBc~#PL2|DtkZJ+}+?#B)qzbjPf3;ah9ujqYC;3+whod7SkP00vq{i$v=@LW8wWfw}vVu^2dT%NoDP93Xv|Tk%%14`j z4|>}hnWgb+)lhriK;2ssQiMRQ8)lGgy2sEejux(I>}v(Qfr@4BzGf;4Tg4IeC3YxZ zl?EV%lX1nGsH8)f{fyZ%)_taZG<^k0fBg-!>--gK?Apu)Go9Clx~eTd6f1qyJKZD| zm)m~G{i&hQ)Z@FY;q+P=K`!aSh>o0 zdUb@$=8bV#ApJyZD`#ImY2|b41syH|Cz-`7XNmlB zcbnG>QrR8I3-|0E0eG*Cd!M`bHP7FkOP!=TEC%w;VP&i3=v^K-18@a$*e?Oe%}TaX z1&k-P#UHTU-}Tbpu_%3-)Dc?7*m2Akd$o&r%yFTqP_J?Wq}{L5j62sFF>r5Y$ZuMf zV^u^tGa{Vc!SUH5sJ!+yb`5F{XDwh}@_VtYdrV{_r}e_Et~mdvm>SZeTlU3la*tw#iQW;Sg&fc0q^)TNhH<%{K7J;)SL z%w7-B_EUJv441Z8F^hOpRQB}ROCBEI*B!_}GQ>7Qxm}$`~03aH14lu)yPzw8%kHQekRm4YyUteGaC+gHL#^ zf*(ML-}ZqlcdU$}EoSod>shBIm%FxeqkQIA8@+7yx`l7vIH_un^8Jo-~Q~ho!L7)(^#X9oP_9sZidjYlPU&l#~r1 zR~T*yv{ZFW!)qq9d!Y9k3lan0o>~4&&VuaQHY6>Mii;m+vC!b8%NxsRG1d4s3tcS_ z5|W(R>0Kdo=rhOO1n;SC@R|&t(XK62O5wLnfE##=)FQX)u5pz%^@oNp8dxVM?OM1P zY}lU>ts$c4arXu%ljmV+$@BTgu_bqu%G)Mp#T>V^%~fw$PBgknU+9V*x;b)aR!z&> z9WmrS)_-c7a%I?A<_wB4DzIr&J&fH((QY{(HW50kKfX6947zAlPnpm5mKl-e*uwdM z<}WS?cDVdtu%a$@eq9rxlvZ6}tGC2E6ZzxE#Id!zO7(luIDIOI-EMpJU7|FChKD;W z=Z%Z{DpxA8{0fn~9aN!0k6%RfoK;;05yN&#$O`QEPq*!V4td^7@c+7z%;{!1EVoC$__~nfNo_*|+R;7aXSCcxo<(z8DW<`z+&oRmg5S&5mz3q~ ztXS&w7riVR?6baKv0qGp9+X|Zaa34^B_+I~^th(=4gEu#r zwWFQK;UW9M{sApBEU-YC|9bv;w07F?Q1PkTCLYxTbL)q;M^DIK6M;{*-Kt`@I?&nu znwZuqoirz6>E}@Xr+AKEN z*+=pwkVP=N5DnafL*%}XzB==r0^(&& zy<)IT1x5=Ce=nzXOuk%96))DnpDdeU269$#6uxQ0&L35%xL?ZiKFR&X#l#mRzEaEmNELdiC>2K zX|C923e*bUpug%~$r$uhNIKyHR!OdcR zFY)}_6@K00X0FxOg`V#fbQ*rbJYtehrjs#%jjeOOe#m11rKb3RpK?M4{(jb4m1XJ}1?A5E{kbG&}UZ_-15bRqsL zi}rySnS=rN)MxWnmo|3eQI}WUCY(Gg8>(I>T`*qH#99Mm#pwvI#ZcO^>r)RLsZ&B?3SN7fqDCnF4>S^f`>MR=O;Q; zk30sB9-rVYAyNCjVlnO3z7>8@HQbUkY&C`uV!%Gvimen`|IzzzX^0T+=n_}V zYXWTvJx84f)NLj zSXN9P&-@7IDDIP#)m~?2Sq6sxy#fD^X<(x^2;Zd+>mSGEAu$rbh_Y`dpC`NMy2vM z@;bHe(W%j97&&;8j|79F3Qs2%t+y!iRyRv}VtU?RC9j5< zJ75r?!~N^^?a!;Wz1wy%R2*ONG^w9P-W)BD7R7}ho%+-v$3M%c!#O#6JQw0I))8CW z43g>?i)cc8e*8cqRKuu*kd&r01x<}q6}oNrw)jKd^@U+NLM_pF+kzx6ubkOs!ATgxCU?`m3C363c`+@|)|p zxzwasXTu&iQky2cDs?0xRIJ92_|h39maS?{1G z`C6o)5QBuxGJgiOq4)~pP+Jp$VT-)D$J;_E6;cO|M}9_!WS0yELX(1-TVrTXU= zZabrIa{>M9s>K_)hjx3aX{@D|KpMD*zibN`=gZ%F)IBIH%x#G~x#aWWRvgTp&R%Oh`m&$>M(dAXLoQELyiHwpqGki%#DblV+HlKhP$gukD4mWl za-J-DCg+%IxBgU5;*0AR>C*?xS2=ZMy%L1i75<6(ZwQXFXmYlvSWYT4Q3j0jBz|W5 zZxdWcm4r|Yd?d$oCVwIOYz0M4#yfva?U~re$L~@FDrDcJE3SD}l?6%kO2l0W$m zFXcw^Mbn$EV#L;(8A7V{fpvqGr*P~#%n+`d-!}wh`_d#jN%PdS#}MGp9TGIEe>6CS z>a5BQj&SI7Tqj?Y@xO;q;XMLS(V!~Ror>6y#wqgli5 z-gg=~P4p8&pr6-irR50swvpB`AvjWQqnkouc$u+un&sNh)vhPtPj{MFmjF+~JiqG5 z>!UacyHZG|N>3<3eXkDTP;z|vm-FH?spQX(Sjjp#n@^Q#jw0z)D`GY=@H^aJ)}e0R z4|J;7Wlk18bH%>G0`;r+O6$M%a77Cclsd1>)o;<{P^MR0SIzsd%3>kw(cSYaKc7gN zjV%s}$Ep&K9L0+NNHV=WYJyhKe@trw)T^ZwB5bB-+k%G?njP z#>-FVg*Jt3xVqYrOLSA?y;@sMORefDMo0<{Nr86SlM|z1NQBpY zGu)i;oiNWA=q+_G6g9M;QVV(ZiPo^V_{sRwk?6!uON;(xGyla4PcYk<+vqJ0RCH1w zu^^Be-rl!sx*4Gzh%-E9@e^Z!@$K^KMW%DTu3qk{DxmJl(?2E&cJHP$&>+sSFbz~w z#uTSxxo;0pB>apn+O#@8_|(~~*c!d{GHou7V`8+(Bf z&L4G}$rOpmXBglbnUs&BLA@Isilv_cLBZl=VFke7GU=WQn&VG%0o0<#}bc4Oso?B@i7A6r#DBw*3rSwCA2U_9#_b zZPfW&JDoB`<-)))uSX7IX!&e*iXV8)GRdENR-@~?oiA?}@_BH8v`u_WooSl!Nsp%6 zs&rbM2wxN%on3i1{(S{)E*{18CFq-w$gw5G-!IQo-*0JL8$)8mC)7t2b`(c6Q`0Ya z^5xetdofM>o&+$zB*YQ>_%$$XK174?^_r%9xpB=qK2gEGXY*s}Y(fOrX}_+%*p+8L z{hLpi`hv=&j&7qP&h7J0h+`rDPE#dWNx(t35X=U&g zfmu{DK*$`$jM?5tB+Pc@m+Je$FdpYzhhlzA%N^9OC@a)W(4YN}z62)L-m&6nXhx{@1U=4(s|pCmq}FB@voiQ| zy+8&1jjy)7^c4qk;RqWRglr9mA7&){d%3TPuf~d>xJ1q<71DQopj7EtEm;YaD&I4E zCFFl`D(v*(yLV;%XyM=T|MyPm&#PThvTeL_T|zIU#j37@lUIulrPZV9{~UX$V_z$_ zp1ha}I>c+R%^v^yS;h z34UtHY1%2~8obQD%(5)n@BU!qj_IuHGadNYk`PA}|m#w2|*O*i^CQ=^=Sh zauO;uXaVmeWRr-O*5lPO!eyz1KAn_7^y4oBm0K+GKJfOGxk{0>{C{JO#6%lP2+<2R zz$`Vff$hD~naE+4M8`$S!Tf3={n#DyqJTb%B?QDWsO1z#s5Vbu4d`W=C{nh}tukbE z#g3A%nNy8#4FCOSu>Xl?9D4WduSv9Nl~K}B2rJ^)G*4@ZW6-yt;>f6e0fN`6c5=jo ztlNF&LUKa$QLBkf^I4K6fC7#Rs4uLXg|~$t>u-%y2ldex%#EJ^a$CxVE)#X zLLA%Eg-4`w`Y>oDQnx=F5VFDvfHNo54#+_>s;9qB^=Qfy`#UBBSMxk@m*aVD8pQ?;>+N^~UOMq|G~)ld9+O`O zriGBnJ%>kwgh43m{Y}w_bxHRSQb+94Y3|a2SpJiONBOV#PJ%Ov zDV;FyqhkJS&+`v|_aXU%m>R`7hM21a1K)g(yfwRh+8o+A$Cs?61LT}!qG*wk@}PWE znS=V3-O5s7^WqJvD!m1 z7~fNx_CbOgKr)SJcIV2V*amU$A$@IPPeaT8x=pJ@aPq{FHFh;DB6buGy~%lcvR1gR ze!LOp%-CJ_0WUD)Zz2f|0dRDh;Nb-k5!lk``@Rq5ripCPx(;Stoy53_+0P>jd)&ht ziuvUE+pK)tB10OBF=&CsH!ts zujS)8NU5p+c7^`iGwK=uo&prtfTVUY4HAR=uq9Qu!npQn20EqkVJ`9Y9i;R1g1jN= zbj6`)L3p7?q)5QGui3@iHo_^0{=`)jPEkh2?#MhLtkS=;s|Mh5i2Fe)8KOaK=Hwkfjt;I>IH?tZyMrTfOXWKSQ4=?OW(K8tlt zxcTY%`gl=^5nHb4e^y!kksrfFk-M)jWEi~xO>)PH*bWsM-4_7Ul<1?(l7aOKNsUPS zRY?Lv**6}U*_7H^husqaLA;@YnijI+46rZPgE4G#%aSw}c5vZ8R8uEOjFE-kIJ{Vn z7o7e+*VII19Z4Owe}t3!`wHWx7p*n3rNhmxU=O+)!zlSp@10?AK3QlCqbAKKBs5>n z_8|8kxia1b`jj0(k428hi%tjFBD6Pl$NTx6t-`2Rf2wyj_VB6W6!aB>m@&*R+Y`^^-{d|?srQ~Yr%eeO>5zKcmI*`%nr zsj>zH&5EJCTmdw;H08Bhy}k5YR{~jgvi=hA-_S+95%_TG19qbe#Q%!wn?bYjHu(E# zm@wIMKLLzmoKgif66Qx+U&ZEEC6C8xyO`O}S7rMK*G zW0rzJ;p|STfauivu`ll1bnia&wE1XeQM!ydQ;ec3&(rD0{@{tu>h6{H2$@%dfKqpf z;;ap1flm(xsv5qi&Gtda`%s9uSC=%;B>TX$1u#*~c#w^IBWk!uL}%0Q4o@LhMq$t~ z!_H#wJ9lT;@WV0IIV;O=k3*l&A|5S9WNAJJ-|-BlmV&9hO&o-Dc-W$OTo$~_D81*} zua8B?bBl(B4x47YvvE%vEc?YxgwOi2ou_$cZ+9X2PP-=`!T~rCo=~N6WlHw>^9!hX z227sWu`)YSm~QFfTTy9+EcGGR^n=LZrJF;8d~#mKU0IsX3M1q+0;ZQ;b9Lx!b$#f- z@nLInv|5@}XAaN?w(j)4?zU+;7KGUWG@3mghb}+8fCDsAj;Av~ z2<)nLmFp}^7KU)ae59O-l{h@PrvzkzxG{nQ)#Gro9XVbL&`~%h&K|ESygV9@g5B|n zkx{UIi-z{hj%_wG%$Qoa{FAngg~O=K<%7=k-j@7~`51$c>%N+eujS!mqPRqZ9aE2k z7QL3&t;x*UII9Hh3yyD<8nK zp9OA#GC5x)$nJt?v{gLf%Pmv=)-Rm}r!5 zp}dobv>=J}%vL;ptS@zD$|Ck)=&X^51=)OyYZaKYb9XK!1mD4Wz?AP)yVnPaHq_k;T^pf72(hv+||D2{GT45iUNMk_&p)GEiw{iv;L1AcHX=#qf z%mgpUP#0bN%#@aNyPQb_ZZD={RTN^cGJpoX2vb*|3wbO6%JRvOsno@5 zVq>(0*iRV>0h(c?RUUVEkStq_7|)AqB#%m%I^Wq=rlltaWqAfKI2m@+t;N*C>gUTB zDSS~k%zY^gf&QZ0TLae1%%*YuiQjDxD?ldFfMCWD>@aA(ZYKqsZ z+Pkh(ahgK*5%)urLfRc=lED{$sBv>=YYjVtF8*NF+;>ZfP{YN;{-w$QX9WnbDpLV2 zte!507_R4NsgF$APgcn_dLo2dmZ{4nUO4ne2>Xc~_o~|wW@KKS$g=vJMB1{$3yHs5 zKLHU(;pJz;i;>ls^*oJax26*WYx0y6%}@8oXz4sFMK>ae4L;&T5r3MQ3A?^pm$V}E z)h|20$h|N|Xs_3(X)uw!tT2alpU-}bchougUPVEw<7x==ZUpBwR4$lh&D`zeO-gYV zMS9Y3RuRs%)y8r@^1crwJN>lsI|<;kq$lKwBD=dQPS)1<8m>+Dw0#T#TVF0de4eMv zMK1$1g7>RoR4uyedV2Q#jC(7xR8?O10$l>esBS&AjpA1{^fKSePuZJZDDA~|JP2Qn z(T5g)_MrU&%qVeaN<@k7=m7$Kd)UkY$=3m1S(IKwUu1+$e%F10jNW5hLo#|jVS`+J z+c4Yx{D`!@^xK2lOvzxzln$}Ord$2_5r>@(?RnwTAuwBUIY87r_y~hKp{M)enaSZD zp29lu^(Y2TdGm!xo9>WkBY48l*aL`S`(yMGzgYXzTo#Od1h8V_0Wn-5G4DQnfSY`* z4SvfzQ+rx%G2Sur!OMx+CgGh_S488Up+Ck0fTe1c39sCD{@v~U;AJvnD(Z8B)eDOb zrcUm;8sz9y)RphNX76pJ zzbHttsUP07o$T?xJ+IX;Vmdf(@2CmeBVGZaxr9_W-VoxSD4M$DiUomxCQF%lmsS zjpV>|ZR?StU-9~$ubKC!dR!zy4i`P?~J?Jbq- z*g^g2g~q{Fk+W9moNo7RZhUXU%{RrA(?yju$&TQ%WN=t{awu7x?i@cqpL$l@9vvkQ(ZKG7*QKv%ueM#YCAEtm|}&^eKW;{%6g`$eqp)j z;X|(D(!)Z0FF(cBAEeW$=)>=HCq#`EJoS`=2eWX&i;*NF7eqN6!3%5oK((58`pg`9 z*)p*mDod@*1mZX4CCkXv1J~EDam`1F+%v$eM;G?@xRu#9nH>M;umr9gag@j;Vi%og zcI|`~z8KHH3Lqa71Pc3ux}TZUKX!~;At5-%&!PKpq$0wC?9%>?+_6jh<0umaP`AkG zit=&%)4&N57l`3@isA|g9Y98Fo*%<9GJ(0jURnWpqJM1#^`c>dk9@;d-v>8)lJ#7& zreIDce5&!SOx@rRb7(M5{NlVT8wpXPkd{-WF^3hC)eX?!jOG*$R6)*b^5@tYdyU_Y0?o8;V=F$i>E4l{ESoce2<;rGl zFP1iTzaND{Z+G*2FB6|>F1}2uJTh1bLb6qAZVviAcA+nF8>6(3g4&# z#v=g9KDVu;UVYbsJdwMhs-2xz6yQ4V`Iu;MXTl36y~BnF8q3N)DQ`3SwuCfpfQW#( zsqy+Ergy)Zdlb@}zHM~5gtU^_!28 zX%nO~K(#Egm0EJw92d5*P>Z|Yx+EHpiP}dYml&^=TB*r`R(R$A@W}=GMrG{h0UY*Y zeuzJ#tj&$a7W>iQ44cW_`AkhwS+i)`Qf04Am@BqcVL)F4ok%en`P3~j+^&i=h)x0wwUX>(t9E|znRpat4PRZvk zO5b)uio7o7xmCu-&=ETF-F!S+?RIwVnko(*qHNiz#E0wagQ{ZLo6#7DqeL1+%u3a} zr7az!lhBu9r278!5u!7Kg0BStolUa^slRjtE2X|pcd!q6)^|}dm`XoOM;iGmh;<$} zjV>a{R2%hj+fq`m#(4ER`>rA)xF~j6(mAKPJJ>N@DeImk;!IloyVa97F~-OM*d$LP z|8#2nwG_Cbos^(llA}G+Q%zQ1RDQ#_VRbjG_O^g%Vmvg)0MUCQ>uh7u$g$MM4(Dwj zxhaD*CV2_C#yHhkrz$z}P&uc3TG;I1_cU7WiZIEce%l01Gv24HZMK+1ch=jI}Xv2 z@yXh(IFy1z0FW{#rnCWBkN-#8NAHd4nM&!wv#^`jy=gxN0{4HEe-Ss6qfw2(d!PDdw~k+=&9`8`>nK+bFH#6iJ0HYuV@&uU z?DK^$N^4;mg>Lh#YD9kPR`{aNKfaGhUo3>V*=)|?#c|gDJfr6ebF|c!ZHpU`gg@@3f@XjYgf=7h_G(5=OY4grPX!}^MKSw8L|}>074^C~?O#els$>Lx zo(3-!f$F38w2wRVDEOV^ZjFg^1S=kHI{2dXOV}Z?Gdo@%vZ}rpOv>o7x{Ga zTbx94Dz1sVV~Zys>HBt)QQ6oyA7ScD_lv)-Rj}6|;*mzhs zrB%Nkdl#*!Pi{Ng%s(#rriYpNbbT-5dP;kzZfPWC9mKDny<<6g!yjaz{3HN5>qX|zK_yyB29>D04nl^Uz9QV8_+njq-dk9uZ+2dgPOp$rZv8H9vOi#vcNpb$ zfg1#e!_?E5_Mgw`O%LGWJ#3JD-$-kU5)Z2V(JT$OuJ8|{<+DI>^Vl#=6o__e*aybt zX8;9p#PXv5ghVTt?1T6LK*$>=GMVrK`+I1-wBU~MS$Q6~`DG^JNqN4!-oDzNTv0KclDFd=oTUYn-%Sm^TVIRJnfIo(h=F4+U_uN#XF6)?h$F@VkT?P{4jgM)C^F@@w%76Y35=31Nuu+h0jAp*S z)7eTrWOJM;s!k3DYenf1Sp21WI`ta$$Bz!O@~lVckNWLV-+>-qV+>5R>xJ0TXg;GR z;RcUx>}{|WF^ihcj|bU9O6K!b3bmobVUK|-9l9^&g_XYpoIvm=BWWPtl;zP}(LbAr z&t90KwvuCdPqpM+dAgM;+P7Je>zB@Ylx+$pa3d&XL+ZAy9@`opb4%txD^%)^R~f?v1@4 z)m%A7f%2{189Dx8PBU1PU!SicNWeAo&aje6qOWtm&VYg;^q4t41~Y4qHQuvVq!Y>s z>a+rw?zGa&-J7GjPc4#d|of)H_-$>R~%$-6c`@Ve0SN|$_YMGiCg zy4MHGT27cv!j-q0iHk973~7#V zOGtUFMNx^a8zTvNlpaOx5~oP_H$LGs_<(h(Vupu%wScPw=W7N{%?|5^qOy0Z-f+jE z@ty(tb6h%X%ineH8?9t}jtId?E5J)#Xy0x1rmfz-QNw_seQw|iU9aj5Z< zOfYWva^LDqjF#peV*ZH~VU81sWnTCbYcgvD+*o5+|x=w14BG~n*# z;DxgiO1T-3Jkx66JpN1}!-5>z6Hs*i+(CoR(gN0#cv}@i!$$9Q(hKDQysn<6tq@D= z%l~8StD~aq*7gyQlu$rK8U#dIkZwe}OQck~8ze^>q#LALN&Obu-M znU0MtUg0Hf0@@s$+mB7`cz$j7>^_2Z2DA`A&m>6{``J)bRk-}whz7;}L1b|62h`ZQ zp2D);sTf!DXR_rNhXMXPE#GwolEszI^p}4`xQFn{&5HP3t))-lI2pS%Q5bb z@hEAloyv=K(G(!vd}S}vF$xyhGwi1|h@3LE9de6#Y!LRzhO7`L>?Cj%<9xx0$=UDd z{w51U*mal>_E;61Bz8k$^R0%?_=~T+!@>gbKAGFyOdoV~QLzhZ@GapS7nj-M$qzp& zsa9cjH$RVXQG=Wi?)k{f&Q9H`9odSoT&zix6I!q6Oy&rP8J<_23 z$ju#2c0A!~eVAQoR_?*@{;&!>Q7WF%OV0mj=Xt{!t`rgLpioG#{>CxS)LHHu@QI>U z?q#P!dWz-7Dg0V7<&9+&*vG@(47>7Gcc}BEh;7t-wK;}}L%|#Hw!LX(rK4*TXmJkA z8ts$n({q`E@uTD>Djn_zAJ;Hqp223}KSF4)?y)UOox=K6d_}a@4FZ2SUinM(@B26g z&d*uq8`a6mi1n?QfB+Mw{>CQ)59Pdo(VqQYMrOcaDft-}Ilbs>YDYVeeg9>p z-|E8ji&SfSkxJxgst(H_dy5-{)gbFr?Fbu}xZZYDTC)@Z1^9f4e}x zkmP4^8X=Y^q_d?Dgo;c06tMk&T$}d6dU$^Xa}iZ!2dWR#%orMhp5ULW*SLNzvlXax zub^5fRU;14kD7@|fvt*FI&}V9-2G0btyOM+sonoLvQm|p*$E4$8N=(;34N`xdU=q= z%Z?b9Ajj370$$<3)3Q5CK8uYW$X2@gy$q`^v zx2p31ob>1Bw1!j~gJT@~iIfX{I&&>WU*AbwuYKP3jNN^FfSYY-9e}mXs#1Q%Uq~)q zWrq>*(f{q?3wu}f3cv+g_?!}=r`T4$-<|64a{2FCPO(3qG2CWz6YShL!uUGFwfdXz z(=UgxwOdTnEjrm4bY*?gV0Ef2|wS8U=jqCas-CHt=NKD zPm3Y)!e~{>9RxpL?AU&9P~sE?HL>j31(kitrDwh%WwiQcYM@(+Nq`K&BoICvOd#5_ zF?fQp`6yX&WJIFpSdE0Q%X;5($tmaYM^a3JEg&fSAIEBBY%ANt8?{Tt<%^iTaXj-ClH#t?Vd2J_V|C6-5=i)~A zZR#2ii!~=gXrX~qrM6ch^Zl=Dn^YrRyZ4dTjMee@zD$D!x4!seu6My?i&Ljj5+T+l z*gY_2)!d3r%V9d|XS-#iQ#Sg<;4`95G!L;TZ@c7d>fL z-w*0c8}@q4xf4b&w|1>hxwDnJD9F@IYj?&D=;&H`|E5l8bCHhCvN zXca;I9tZ6nhRCy(;O4etJM=J8sn~BoBTGPrV&k*+`vjL|J;)L zS&tG+B3rZ6mY3}d7d>Gszmh!+YXii7nwVzH>$S-51E$Lf8p>JfqTo~6g$*lBHK5?- z8by_g2M@lQ^`1YmJkX{hZO)cR-!9I23NOP)F98aDVq)#gCR54|(Iui;h)rm-ha1W2 z(h_6N6Gt`w9WK_YvRd2;DyW&JQQ|Ap?rgC~cw zvkY-!TiQK(CO$1Kms9Ym%0W|2*{A>7N%^}UO?Sv8o%`|Ec&xbDB2hvx1)DE=>alj* zru0k?CQEx6CUbY;3f_Z*}fY(;+m;*IS%Gf(CL5PYJabsB3wC4`B2YcCMU5nI>%XS4JYD-7>#E~ zf6uCin(*?^7xu+P=;$S#8wN14RAZ{vV>NuI50wA+C$_N%OWs%D1;D8R`MhRy5`Bz? z%0aSyZ6fMiftdb*qWr+wMK4?HK0^odK5nwY(348{_&r>tML}y*<*i4SYEyik{PdgY zOd`t@DdJ0YxmrvDubswfmhPCyTH!Bh@!YkECRGEyk^(A6fK9MqMgE30dk4Pxi{8iQ z>0;rH`*c>otqVr@iUX4XF)WDC_5noej$?~u&piu)PFclmC?%HEiKk=~_B99=v0S=iq6ll4QcL2SR6p9u_Wo&A7z2~hzunsZ z>o;u@(VwddVGE`=OXQ;W@#TLK|HS%)!SN!1{QWBeVc=D4un%%41+rDnwra)uJ9*9E zU&82;P3jY4l03RtxLPc=D97>{(EmbRXECEOplv-6CP>RI3!=-*0(3)1%td-7wOJ%i zBSax<*p$ale_AH0q*Szr@)u?<KXe)4~}RFaPSg!m66OQ>}N_4|66 zS}+HT+6<31Gw=f24aYYSlbYWN@1iZmTXUz&T>VY8Y*_TosH!+=8zFU?~3sn z$yn)l!VGBYByTa))RHfbN)L(`L7yrR_|Iw>C^vMQzndVUz6RNvp!AXV}5=>Xxa zwca1B|H2Gn`M1R4UpwtP&(X<5%z+lW0C82@w=Rk}xjzgZvZL*wt3BAx{nf9pF>zJv zk3;w?LIZN8!X%r+UnQtg)61eo32SywxzKeVm6{o4ELZ-)ZIKlOXazc;tHV$^&?tt;kl#T5vU1ay4|aI6)MfCD#J z@8-WC94+o+{=p$Rbvus83F33G2Xsp6F=A^KNQIE>y>5~$jAKcYr_g+j`M?XzMbDrM z$Y82QhD))~UL1)R z(Bfa#LLV^?$h5`vrX&%TCSGWyH{X;k=#(0}M{E4-BSe4xH~l0w_P>MYI&rkAs0`ijs-HzWxbq1Df9>p`ySWwU%yM6XPp3vH0fHG~!Q@iVw_0mYowrPY0 zx6{NsUZzruGu4&?BLwCZoQ9f#wysQN?>Lkc(WWLrP?Oggs-knQ3DvV3$#pCl3uE{< z@XO!F+R|dgA3WC*Ism>t^pAKlhW`wLE_TUYLZ6{%9gRh%>cp%ePKoBiq%^FaT3;Z8 z^PZuWg$rE{p1YMen*dFz(W5TC;sWVkn~|$|qSvOgr5bvN5L|j$6{(6I&#Z!hdvxXs zAyDH!<>*Mwzc?U%?JK!p@jGpF>N4ub@p|9?o^k(bp@586@(HLa2s_RCk6|v0cKJMa z*KvN>uhGn3l1zg07%Q`nNX?pUtosKpj&7%Xt@~$r5zW3Q0RZ-*i!L@>l>A=+U>ZFj z09gZb0NFO*9;b%t^<-s*em}cCWb|1s$_7J?ebx*JE>`eG4gw?X&vf+aEq&a6(9Ac@ z%mH)TulGQ?(KKSrF#BIaQ^=t2Yr;@Q_o(wSg31W-<0?r zv-7eYLKz=Y*nDlMc+ey^IS=HFDuC1|>}10-%N0`D*t6rY-Az7IJIHR&#@?Daf0MIY z(Y?n}Fs5>3C~2 zzMWQ3q`2LD^4qkkdr0Nbbs<>b_&gjwq(Ia^aXrMWs*&3c<2g&1ZV7GHfyOnlk|2!M@UTW zwkwXmtd=PXU-qclAIVU;eK~+B#$uFEX%6B%pg8^k)P<@!TIXsn`!KKx6usPH#qWY! zkh8l}zTge;C>G@m5^iq_lPfmmwV8Oni`qun0mE*Gufvu9_rm{Q<7{gs3i#dL{uO0A zNW$ui){K6G$x1G4mT9uqxJ29~6!!vnHjq{azjY$KDBut$rKOhkUD4$74{qM8MNIUR zmppd=wDXnqYX!l*CusMal#Qv|V`B0)J9MchawqjCe<{=^XY+?+N&bUl?v-Mc;bL{Z$=0GZr5?CpUylSkLw=cF)|6XO~7 zzcAv;g4z393^O;x)Ek?q)+JX_o2m>CX3_F?%dAtWxl8|PQb9g0wK>{Dyf*8VxLOAs z8qWxA=bP#YXvI5Jg%A%!4mu)FJYXI&Y&Y6vQU90w46J2aD_+2t%a)74+Ev<{?$7X% zXYWMiJXW%X$o&wC`U)6mag3O4^rdb<`1Dyc7>_)QK?nQ!_?vPQ(G15h0&@PRm`5WY z)I?4VXXI0v>?WWf$XM#jt)nQ+&)|&oOt@d!8`eC1HYXyqV zu)_#b9mXj6;CBmumeEQk)9`xfy=yoL`*qc98`8KaFY9Iw6MmOn!`~Ax>OYU=vFClZ zcau$G$HC&vY&bHpBl3>%c4=j3pUQTKYCe+R{yhoRE#*R{mE-)YsVg@dn<2^!0@Pr| z^m!tp*J0JZD*5TmiYtceWXBY9p-y>hS>jNgd~4hP`tPnnlFIiHTNE(B(s<+xB3P{wGm<& zn}UZSZESs9X6jQ!XW_Ghf}6 zP&bEHWPB|4nJ4C^%y7nQz8W3!38i=Lt*3>>#VRcrWVLPRCna+Z zzxFa&tkm~v$74JuC#PY*4q9?ptVaZFrzYOSK2_oWFVPJ#3QaC9qgi#CRx+&rh41G+ zvH`;t+Aa-sT^f@^qGP#qB}5F+i4_E&>og>BtLRW$0sa0Pu64j*ePgZj?mw zUTI>bWg>MJ)I%F_l!tFnFB2Ai*nd?>2r8U$O@6dEv=d`j`>b;Sm(9?_uma-8LxoeK zGRsBiCZzuLo?Kb0o&^iFp z?{j(p#ws{Sh8BDg2v8zOdrFiSlg6fP%m*r3Yt@|O z?xat&i(r7KreqFQprFm{Le@q#xo_5Y#)LNQvdZM5(rS&Y(~DAHX0M$B>e@OsR{%5z z@kc{{QqSa76$){_Xb%+k`GF4dhWmfHmplP#uvT&AWtj9GhJR4M(2t`83uGMqE8G~T zGJOei$-Li5M&|MCTa#FtvcZv-Cs~ia!7jEAHNJVqSmno03(nspd6qR z6G6SWodzQPK6}@?x_C#yTX2-Ek7~Us5Lo9E%%M0KwcavLOZki@vGR*sYWRH@@g>2GlZMZW?R^1^1_YUVzdoFDAi@4_)|nP;t9WitYD2XpqXyB%5d zEhX$ec?Zh`Ouus_xGn^N235O|FdsDlompq((XHdL+?~q&h~UxY^7TRG>3Xc3BNtFv z#YC_FBH0=%Q3HUcy082%@^0^Z93mH2B02tOS7v7|Tqe2UAnQE7{ShF8 zN)Xip?Q-9*G%~1p&qdfDpeX3>vgF9TLMmwGU>Vp8;j{E}8~ZpuiCexdj~OokN}=P} zQ^?|nYkM|7eq}cCq8Y$uc0B+ARR9vn|K0T&!8u9ozOe1|Q-zb)so#-6J8WzhLqqFJ zl6PzJime4aidtkU#fYaN|0GMm2M$2<#Egu*PY~jNhITOLmxJZMYD#bwpZQ%(n-pYj z?WKw<`BU5}6`@K>@G~Dh{WB5XkN-mx@GwPYYY46A%&cRr?3a=CnlSr%9X(e1ch<~B zE8H!9T$SYAmB`t1v{ez3m#7>2JLiOAFzVnrk^;kdycJb4WfP+d?zYI-2uRmB_(0{c zJv+pF?Sed;GV1+eKMuvd4ddl~>IflMK0tA;2lg)p_*yi}p;SQyh>1Te&X#Fy47*`)0$_y)e;DlOSynqg-4N34X>R3Q*6trRtw*{V{Q#3gcXA|Yd2 zt&(eENq*SrdwW4*Zb3ymz;BmPG!X9UexIZ#vdyPnG~xQ8>SFCjLbRF@{a|u^W#Z-f z#yLGd4-qyawE3=(^%%$&V0o7(PD|?*a-7Q`KQrAlCj9b z&d?H;j>j2}Hl7Q9u~~i_+uKSK;&j7=5~uIspv4vh&{?bnV5X=7hrYcV(CoQ7Hd8izkfh~0OHIul>+nJS_)q&FjpTqlcfD7e$v?=CG+`G z!uKA)3jJ&*gwEU_0))TYOB`JSab;1ldzh3ymjgS0`spyRO@Vl+vAF;j=aZ>tPcc1a z$_a)`Wwt)^&y&sqf`6t}{}htMq8q^Re?|P4kR%)7;|{efa^0Vzo|25$PH8Y8GX0V4 z@Wd8DUS(N6wEG^na+}IrWO09DbEF5kN}Fhh$mp590>amLQx;fx*efu&vUu?_``5t+ zmzkK)FQ|ytSyZJO+xKc|jX-7 z1hjv|Y_yrNR}jpnJP^=UClZt}rL^$gyLS~E9yDx+#G*2Yi^g|L z4B;x!>$pin0|$tHBOw6i*NG9_I>QWR*VnMkosiD8zKt67(X-}o3MBY=pn|$Ye znB}aNShnh*tSvWGPXczvDuS3ujC>^MRRARJ=AY48KSIWKCnpjyknm}sC#gBWRomC0 zD$QvLXox2gP$DDU&wRXBqxtLpuo;|LGBfS=&rhh`9ER$r7p-ny&MepqUtMzh?6>3) z9CIl3jYGvP4?rW+szTeZs}|eBy=GomI9O8*PA=+wvYDGINsjaJqhOZ0WVtXnXoqK3m24ei?53xG}{Wy zyJYwst*4fH!^~4^03}H9ESapG`_EedpL`~;P^(+{Znnn%PKSp=!oPXm1x}4jaa5o< z>?Zf;A9jIo*!zjBRLhLLz^e~1^xuk?t8`q99ZUJ~11dQ5DeEmo2m(k%*~Xua)B0bG ze*d}i6qr+Sw|!OC$ew!-1?~4JgX1^=cQfqMZm7M{2Bv#}`PR9N^g?nG-9be5C z*p$0n96t0I$qOr4d8KFU&~WMXtm@3aa|5YEbwgV_=Q22ZpWJAtX@UQs`DB(JoBI6? z4qfTC|FT^^fC6cM2ez|LHT(oIaynvyysF6ohD+R7i!dCp;j&p>oMlGjt%3Zj#ad@k z2cVqG4|j=nKwPMLzW>#>f-~`j7Fb$(&3_k84BW#iA{TA9Bq=uB4s(LPfab_}S))`C zq<&J@;8lw!7;a&ku6>a`caIkmcNIlj4ywOf_C?>KooUpL|JRCSU*ezLCDOuJZIqH)~l1fZ9S zT#bL0L@uk_EZTOI&j{LjLl?ZSsgY*W`sSADQz$^C;c8vltV@yYqOLxUtbddAMOBAXSwcKeODrL>$nEhR#f zwd$dSlokMGW)z@p*;6T1pJz87kkn>qr1jBS>)eW5cCA(>L1l+imQiN9R3|7@2R=s< zgmE;RmxrkcB4@Rc;%<0Z3y}KaC0I^;PPMM!{WeC!vs_y;7UrU<${J z=8QiyxE^vcToXH8eE=DBvJDVp))JSM9_iOytVA+t24JXT6Wz6Q0%eb8BmNVB>@)j^ zD*^W4smZbjZ}fO{llwsI0CDmVwNvm9;0$R0BNX~I;NEJ``xngCk<;{HB{eP(bjz9M zyPk}m)GivM9B!N#>1A~&W9+_J4pASq;N9)}6Ru2_ncDm15i>n5E38?FR%lC{;b92n z%g{)1X=%DRfiPMaPgag>I#)HoB{KWkEYm~)6|IlC+6iWvwExjLj0|%m4-mE4V0(3i zyyZ4(&i2`Z*t!pmr>YZ5yt!b&8}00BKIvy+rnc;(TKkk|yHJs?a?0c3Rhw~kN@f@< zb|cwQKHi<|3w0773RYSICy zU{Zq;I1bE6svfQ`{bos-bKMw!s*083(mE0<9td5QqfYEY^CdWhBT(B;-(CGq95=YZx%z&5u*WK-Hr<+G|G!-d=H`aZ`A^_hd$_H(GIqa7Umjt|} z9H7IQJS943Br)(mGqK9MPZE!Jq(mzStz;HWSJpZ6iVw87(>@npbJ85HU16t|XI(o7 zV7Qy4GxW`#5BD2oZf@J2MMOM2z!et}KfJyIP|`}6JZz#?*bYRZr`08^W1r~ds+y3( z<9QqNKBA_UneGn9gS7keRn1(yii(Kd^vz65;t5}7r)-+FStFFE8bk6y(``z=VAAZl z=HUw}N7e&Xx$CzHl!)LpzJVE_R61r06%smVIS&xKX%pMd zUixe*uGctC41RnpG?XaOs8=RT*K)`kI_(w=;-@2K4ngJI%%5}LcCj3YtpS|@`-{Wn zyGsckRCeVyDo#^8HSr`+qG)9ZwM;DczHbu?B?mg6Ki_VFm2aLzn0?<7Ga`Q3A{VIG&irG#_)6K6yFeeO`3D=6DO|H6rLtCukn z?pOi8W1s&c+0|4gLT3Ql5Jx=}-I*u4Qb@F=|5x0r{5=$+8Kzu}rA}@LtZ*MyyCo}mZ z$D1V@-1I4#w|KNcGMAtDqKk<0^`|;flYM-1Yt(VX;kp7g9{DV3ivGRv)HTWC22RMpbGa9iuX!N&!y>=Ze(^yN;Q1a-BpHz-1o_49VgcS$wXcjcK1!DC z4vxse`8vqiI08SdIe`kXQL{(1KG(=t6qi z_FF&aoA-MhV-~Cg`bu>+iF?{9kBgEN@n101I>s0JnIyf=7|;z*GMYWNqp!rHuLM)v zo%<{bRaq{0HBc}YpoDUuyq+TGb3UMh6Y-OUMc0GI+F_N26Q~7&5JS>D+E3U`YaySm= zsN(5=yfoZ;{Svya?&x`|^4QVl@HlWX^M)2<7npLnjGK`&H;CoVP?@soSJK|Bh6=c& z_|$RqYF$b|H#|okMw0e=cEZgJmEgr$dsSO!fZKlp46tmtb=}$*k$JXk;0VR53RFaj zl=;;-ElScIR`d^JSg%p`jrf$4&&u@X$Y5{$~_3no|z-m(e98D*OA ztzWP0xa_K@@{3NavW_kno0W@=^w`E$PMKnQ%|eU%R3K(&sGptChmiLwbDg4)X(JAg zKy#cXR!s|MZr0F-T^Lv~MHIU}O6ju->xXkRARmQiB&vz>Y`h?!RX+|CIBRI^XOa+A ztJE2D{53*CGqO6w162+d*x|LJYy&9hNXlwubF;kbnqwh(nsd0Tw zzo~H$A#ikoC4?+SbStHNYT5nXDBSE)k^|r8Lnb@>ckJUQ-W2repA-b%Il7fQ33}m2 zyvokqrWEmrd)ljWu3vHbL3!q5z1nieB()T>l4~`XbKyAyHQeMNHNo`?mrXN)x}j?C zu3F{(-b4AeJwdPGez6_Ofs4)^-k$b|7sNRg#UEsG**bsDcyxnX+;=mB_*r0$CO^-Q z+(S=nV$3#^q!yZ_C3h4gjk>E<+3U7*6 zS3>$=yT+=r9Q>}Wd|9QNB7S}9o-S|CBW<7l&{X5GfmpS(!}yp7|~uvpBw+_vR32 z8TtAH1~2UO2NTcN4<9?-$2lV&dshtRi}ekT16%1`V%MutAM5DZ$1$5Qy{x>{mwqb$ zj4z*HOy53SJs{6eWHUb<>j5#&I*#?u!<|R;2yP^+jCM|(s0v05-kAa~;XrBZxBX$i zfLvU6*xgg~&^Aao8ssIhuL3tk%*|-gAkqu&r`_n{@Ui7%LumgL`pqk`CEcoOJQ=-@ z-1jJi&En3JAHvZ5WG+T49I+H=jq*gL-lm=&h$JjCR2i51b&J;sc7|Kau&kfI14e4KsufUZ@THE8m-xFzgTrZ@}9P zaawUXCbWShR%+eVA7X%mS?Xwe1k-kT&UrN`XA8d?v?&A1OlFXL1O5Dt*9i@c%EXt3 zk)*6t#*>{bd77N2z7jyjs&?l)M~NOeE(Mv$>E;=CgXK{R^20}njLnNnq8q0(2~9WL?HAb zo>IzOeMcNw*b86iOm;#3Oq|85$?TL*x49g;Zf+iT9d9q&Lq6YUlL@>F@C~?5v|IcU znL0b|!?^H6(LE8@^_Xg49~Hwmkkaqgy6Y<6o39!(n8cY_I@svfe75f!#_K-8s^ zPA+~DrBJv?_>G^G$d6MlVX=QDxVmXyu~Zd;W5GASQFXFe080c8^_QKI&5J zMoQdsn@A=)aU-Y6_x0w|Ce@)T1#r?{-I|sJY!E7-uGt;cVP^Pw4p> z;4HL8xA#jBP3$DV5zdW?6h#-@k$G!>Jlt{KJl^!4%{8-AGg!bjzL2M!b!Ox9;gqO; zdjuCeDQr%ohSJ3se@V;>v5^NbDF zB9~ZCslvr_uEZx&b4+JO<8|o2J&JgIkmQqhQu@*sN!2QbwMb6oNvkwfF@FZjD_E3j zF^Lsj!7(B0?Y_M_9sc-6AsR%Y1{ptE3y%^n;9Z`qK|dl(BK3qavCVb9u$`(P5YaT* z4W9)Wylp(Y6LD>cEr^{PD6<}4JPo$f+bm4c!IjF7gD}C*Ggp!m59~yLyBt%EU27j0 z)YD$J%JgIV?SIQom{+ut6qNbOWdv5%@%<(q_tJd7y$i5U#$WNjHxu?;g~mK;Kb^qy zRNz3Yf=#$`blq3hV!^=`#kW0v1R1~KWL*n)^beZbxEU?wO8pl4(Tq7rA*xK{I`y~f zL(4G?ECOUXr}GYI&Ux~7FnQPrm{AG@4^O|x(RCoDITJ-gJ`bOzqu5#{X;6JhYU+0Z ze5l+d+L6ho0a^7JtMKeujp5hsrLozw>ffbL0+FLYbjSerl+_Qx>kq%{oxfAT6i)l~(SnKSbYAEk zOMFZO0*tl0xskk<=F>T=1FO{WpZm=sbrpt#t06*-a;vaNy_E55rtG1ez>;?MLfE_4;@j0iM|qzOML++9Q@`Zg=B4~S1d@3+*N4_q7=c*$)H<+QeeHs6AC z*t18o$szG#H(~Y<4f_PjbwhGUK!Tbnn_a0UAq&4*#@R6)Yd&^2?`SdR96=w5Yh`D} z2b$!M?*D4@bXLPRV7Q1nY6%&CpXs8OEx6zaU-o)}Q1Jr-YgS`#$7kNUOp&sx*)p)( z#dg#a9A(TL+3|(U<<%ymlcYp7q;=+~>qlz%1840jsEJA18g0uWz4RGMwW3VumT*@Q zvUDz*4rPLEx|o3yyD24fZmKNT@gWN{s^31no>flr_gKSew4Fg1+mG2Mc0$rWP7bm% z*X+p->P4-Jkqfck`%wOpW#mZ0C()phG6^_;C;Eprm8#VqFY@AGZk&4oGN=XGoQzLV z_i#={zCULpRa2l~d{zAZc|W=3!MZG|n1 zlu6&_b;OEtzrI55FGuj|;+%OHR1x1C9Tsq11W_+i2?!mB()K0xv_2vO>1s%IM0?%c zml)lwth=&03?;ewum@07i_d)uyB?MMY~gp5DrJWA*_=LUK;L$ic#PZ(GEW`OYF4dX zMGow%yl?5Wag#BDNqEz?Yuf$`PxxJD3`Vx{?%SzxuUv&&clJcuue}RlMu_*zbC*HP=iC*V*nUT$Q~jR4^(^6Fw8e8V zy-rr%&=*r|O*g|CZ}8?m{ybM>R)VHFXan)GdkV`Jr)G?3bJLD~Udtu-gc{kEsCEst z%5;@BZrz71eBd=;zIdSKa%X+${4i2Ho9gzA_`=9&8*kv&jKkfJD>=#XCy#C6*Fj=N^#^&lp;RcAzG@;UyZ&Q@B!y<6X z`}n9?TjE9>Pwgg4!-wFr-rFv^ zys^|Z2(GK*ADz5seO*@NuB`|6)KPwL4+v-a#4zcMvi-b+QF;9a9~L-eM&cIt%fK&1 zS2>cAp9?JADfR^%5iK-geLdlV-hNPtjvoVzfPrG4b?Xv>m>tj?u+VUNNLq8l6lVd z<1BiNeD5jOsbm0@R)Wt(Iy1OpX3Tx5MGU!Gd=|=V6v8suI|X5PZ5b_1!~M_hB$_d= zp7Qm*UIOZ=4@8s-fikK6>`&)6^~BY7Lr-{Z3J)bERl)14Q6Z60I61@u+?i)yEyS40m%{Z0un={Mtc*a$lxIB7fe0A3JK`(qtBIxS; zM5&S9N>KW#T<46gK?EJ|j;$W}UIml&;xK{l-u6W3k zB=6!9%UnxQB?<&fqCWBFsr8VV>)?Any7|i(0sTI)`$k3clX`E;EEQ!07nXH@1yEuV z%pAg&)$T33QXu3Pn)dn9P3CTCgl{d1BuXR4bARe3bORp6pk;}CABcO2Xdoa3GdVrN zKYwM$9#hFB+G-Adx8plbaF>n|FjOWTJ{h*(DqzQG(&IRbx2`32gd?Os;rG}_^V1v1 z#m$fGaHAcFgrK&i$bGr*v7q(tG>C8l!bkORVB3@bF{h9h;rbc~JBP9Nx9b&Qm%zCI zs!Ds`70PrMcC->G?o1_C-WPYX?<%i*VQh#9r%;UxL^CfkU#FQW)=zY6()Pq$&6MM|F%ud*WQXt~794kguoJAE~-62I*f zcW2n$BV>*Te=JG_iZ$Kkx9-yyi*gl79TeBqa7X-%HC~y7+rOMAIeDw}S?gBX)7>{Oymav`$hV*t)P9lyel6YN0~NEK|1hhLPs`-9!WuX$fR|tC4RS zWS$vteWj6j9NCn}oFo`gOu`Y%bWUtgBTrI^K}akmh&@H?_KttGvY|!osV=YN7donv zx!nFnf2E#X5)(4;n;MRwah}6ht}21JBEt;h^BK^+rP5n^Gebc<-Tdcoi=CMp@8cDA z8MTl)&e>S1{fTf~XFXI8Qupe5Q;JThw_%Xh_DqDNa@j{*;kZb_k{EX=OC$@JA&0zX zc7EO$jOT0yzJm6biltPc_8})n+$(qDOP?`Z-0kA5MXwM0Ne_NKrm}qLr+1sVRL|1- zL&%TNd=rF?f*;0`MbN^(K4v%E0{4DY^EmqZP#v%LGY z#yCqO+*MNo5G9Hb$BJ6^c|njVXAPkNvDY5Ap8Q)zjl0$dW{ zE$xc8Qot~4USL*O0+HNAquqM;;DGeAv=k&1ocJc!81!^qJp?P4;kL23JQD2$FRN=Dh_08<0hbYqk9|n z5|I{K7D-RP4AOvO)4uqSnt%=MfkhX7v&0uiW81jv01Uy1MC0r7kHG3|zyh{_kByR(wt zpKOawDkB#8RpWRAH&FX2)%PgTg{0|B{j28geaK#+H3a1!(Vl8}#n9zEBE!H+j{25I z$D04_ftUP9%8souw+an;)-#jA>dt{D2vOm8J-W=51#HfIZn7UlwhH7ZmZ+A+yZSl# z`a5x)&<7VmD8}YWRb3}1-O`oOzyhtmv+>91f{Nzl+WD0_KINdP?|}3E{qvrnqh4XY z1dG%pU>(aSJiWl=R3UkxC)FqY5EBx>$L6r~U}RjBV3{mj3)}x5eIX6Gxgk#8jum9; z9FGF@Ei8K$H^ux58*TzCFGF5e82@+oEpCi4LLN!;Xg)O&g>b80oFI>pX^#=cN+gbg zDMr@&7PDF4T0MS&E;zR5=Gu^^injPwNa=;Pc#p|enH{VamSd4`jZg6B+MS`qS#oX1 z`PvfDTyOm^FT9EK+=O`T;W0WY*KcVxq6ZX5u9tKeUK0GUkNBB97!8k2msp}@e zI#75*gI`gkV*UGb5`5W4a3vz~4NjsBt(*q)=)<=)4keuoApECU#8?{Bi z<}BqTHnaRDE1V4}a||6%lh+y37l7f^;c z%S-IApU0FC?6~e*d|6r= zlr!2!1IcLtQKLxQACvxtEFOA`&oBbcA!@0})yb*^`G!*KG25$Fr7`8S(DPOqH*NA>6d@4-@m^bH|AX;&YJ#RdtPNGR$Hfa zo8{|c+N5q$`PkR_XwO*-uB19%uM%^2jWQL=%qr|zef>E*>5{rWSpNXU7rxU@gXvzc zA{{MfOH0YDK*!x+`XSxfZ}}PDc@&VMZe=cCd@BgPU*Et1;^sJw-{{<5al%dI@&A_*B(uY9m#_1KJUd@ z{m`oKM2?r`Xnn#mCd+h(#+DGBp@R`DR9u@`a6YAU3o0MwSC^YPAhXdGrmyCC|LPCG zTm9u7Z6MNssChEwcPGzKOIJl*asKE7dU0x^eb(Ppi@EcpLmx(!T8Yk}ttrN3=COa1 z3nMm1)6NlPa&n_2^&4yI3bH<}k5a#`LIz$LbN!SJ9q`f-zqSt3egcsdzxxoz_sOMv zZMQ}LaTTrpgfAh1!-zvK7pOy@az@|Rc%DB+iSY?J+2zJ>eAIu(9r9R zRNcg-&w3Qp&`0g)NZye6JaLZOA;S&imjNBWW4|&FGn9wlg5|gK49vQIrR!DNUHFvD z3HoPH`CdO3It0hfPeT+F2R!Zj%|zaPL^Qd(%;bex=(BceH2koD#NO<&;ldbs{mpjP*#pk+v?a~y&ILJVI&0>W0bvyq^SWEbgq@nIY>QD^j z<&@BI%UY4Wi*eD-*v3Vm%f;QLsD{>gdZmlPB>GEaAnE0)rCeVWoKu$iX=)Z}U@h!h zHD|7R5&9=>0I4t%yc0SVq`EF$zlhLrAZP>_%uN?N+RySuwVLQ-j@L1LskhaukW zeV^w%&pGcopZLOu*|Ycj_gdHbUDwJTlPIk)Ur^ECEh7DE{0XIx24WDR#JsE{x7{NkQ&{`iGXZyrD6Z)VN6dmqZXBf+8@^fwX!y zk0OE!*HywVg{(kVCMArKUo-s2w z6b;x6>gZ*W6^Gk5boZyQ9Z!%hJ;_{;{kw+sw7$>6<@yP9D z2)u0gFc^WyLYeUtW|HyVTHlW%7KY@IztXbqO&3B6E+uyO`q;K^!lQo+aQEi>jeB6F zgm>UcF+}r*=CXhPsy#{kEbrm7)T$}C_|aU{+n%#%i-;lPPaB+~BDruY8X)7c43Qo+ zh$09#)lj(4ZcPjI6$XWeNebeYm7u*4tiJZ&M!BlZBj5eic(IKWVyT)Tc22JaOV3e& zjbCL%9Otpa zrvp8a`d?hk2~w~7K>HBT%qwfH)Tx~Gx25GCP<9-1l7!WDAtITvc9>rYNOA67z|M6ECNu&w-_O9)#&AQIp z>##i_uF+O#KM^;1xpAy#^Xmie<#)%vryHps$-~M?-2Az0z(xurO#l97$)||jaN-$$ zkvoX++DH_NG}@NhdW`l+vCw^uJ02iE!%ArAZzkg$Wq61Qk;_Mr#eG@T02UPs0hhuh zn^?BF%Jfp@0IBroS#zz;TlE}}GTX~WR#(dqA=`DS{_l8a-tWTk&E8PxW{K6W@Cz!o zQkw6jJM-5|hIigurN8w4L+-Eq>Bg3F{O2j~}t3VYKV z^@=@Dtd)9Sqr)g_d9iLSEjdC{Qyj!d4agRkSm;LpV zgj^&MNVR=!MfQAW{wbkDB#S|PJ+?}}MJ|r=X-yW~q1Tf7FYlYL-I1bS9c#y*T=Er$ zW0XI?xJ4FceGm)3(A+d|KoT&doo$WyxJf2M)%arPi}3z?iDf#BJny9<11Q#JIeuk{ zi-Dt-Y%q{zZyos3pQJzEHBf1W-T87Fox}2ck|r8$8p-i4Phi_hc(XC4RPoY3VD9>? zIG-%CISGBHxrPtpW2{&&9a?CYvz1ueKlEz;;bz0}!#XJU%8zS840S5jth><{3fqVntl7V`?ghdJOhjd5vy^mRyp+igsJw z2N<*FVmiD1qu}&9$xsvqVUqb+_w^J?S=pbrZml-v8$Qd>`$mtZP7%jW4WV}b6N#kJ zq$2Z+kKjkClwo)b5aW+=LAM-lJac@FO#hySQ^PVRts`d59M3bF9PM1upB8#}=41c< zJi;NFRTirD^&iokdHI8pN2xF=9bPCXQ9%*UlR8v<#N08&Aw;Q=mj?wEF5J$#Mw4^b zO`zUu&e=}kC~35$XJLA&aRkJGdcFF`8X5ddST`&yV2FaV)k>`Fv!zHLI-Mj1io=h3 z=r*tPFtZ`iXh~#3RaR`{xZQqFFX6CI-<~r*ebq3a`*upSi2X82=I!XWS2=^Nlrx>!L19bZ;0~vya$H+? zE#F3w&^Xc4RY3-(=(hWsuf=9|!QX5^n-b3Du;f>EiJMCA!E~fQ7LH@)d`^~0z~jSJ z)25rQ42M5ivs4CqcT@;}N6$X`Y$>HdUD^#!qL$Izx7h7h5n(Qw7nKbrY+?>?{IPIEa zDQ0?75Qq~~C21uEqwPa&&EQ|I zvThlBkKh>d^5uwXR%jp}==@VJ`s>jCBkO#1dOTL zAErS5-E2ui;I`EecNE$_W$$w}CBk*y&?Ty9G*%g6=hB*^4$C$C;pD6v{HP_>=#Jld zPd7@vb#F_UUzm$-`A@f)is{03lo*aOuiM*9@bohuxlW4v&P$dVlhzqtPn<4>877s@ zA8`;@fM#meiyC^bLbkg2e|PAw8oCxUuTjj&pE*(|1IqbGlD5HdbA&=tb4rV>0foee zzJd9BA~z?(OfIwHRC%QPt9j#N$$GePtmEpfZAa(T?M}QjJz^f`S_6-fZz3nL66okS zTxPX|i1n25kyaa-XfYnHk#5=E!@a7Vz82QKi%^fY7ALW>@3vbL3~J*-Ta6BQ=susa z!1Z2F{Wl`^Q=8wwr@b`&2xw`MahlwhbaA5Z3|IEWd$6Tt)6*J$%XoOCbGboNhAhe+PQW@w3`6rHnyqO={E@{!XsIDShl)v}m7J36bEta-F6B(ft0t zhai8^L3!oYXRLg1G&-qIOw!Lly)Sq%w+S?~`2)z^#7M2Oh8dqkLs@JSz?(=bFA6c; zM?v(w`FGn?fsJ|BBN2=vv0PmK6x_um)IYu(%%Gq^^+J>;-_=&j2P3|*$q_SQTW|z6 z3NMICFBiP>ZX#LiW{US-a(oMqG)nQ!rL=cNWhG0G#e9V^U9#RcZa^T+#*)8zNm(fY z3@ehIUt$2~Zl$j2HA@&hFdC8ygKnp+qjvMEi9?MDgzGn9tFz+2 zGZJNY^?g$CBtC?!lMQFQ{KLPB7hi#8S&?)5-yhl%1;5aV$}ok98U%r-^+LSPQyazW zV=*Q~4XcI*(u7!ls6Y0byY~j~W95gS{cuucTLRfiUlat7d@p1VROMC(*rrIY$s&(Z zRQyCYOc%qkX+0z5Et0pNUlKr7i2hXtBahfn{0FKJ<}+zR$B1_-W3c!d{f{6V_nx6V zQoyAadYRpB__E_j3)Dd|>)35SX!KjDK!8E&je6yr7;N;mGACG+5`mG_yHQ*B_Ska+ zm*QJEZbG}o{26rL=4KZK_kFs+W++E@qC+h@)_E- zX)YPNMMZwTS2cYl)WW1yMY=|sT%<-GaJDtEQ!9ay6gM7r&aX%a(*!-ojreQ${?FF^ zUK-Qn#phsp_xzP-E6*MI;5GvtkkgezMKS41zMyUD;msDkLf(I$fT|iT-iA2cSOeWf zSf2M-P+#6P!(ybNdZYH?&3tb{2{uBA(DWb9Dh1t=7yHaVTdTl82Sx>jB>R6Ns zh8t;TmS!zF?Z|diECsvpty-N;7p=)U@Np__FMKFRmc;1J@!!UN;4ju|;^XGT-wv^u0Kj1g6tN>Ba@6@q0o|htI3Ze@?WJcH za;s79teH~DMnJLXKj#FN=vd|(mnN|DQ)Qenck^mo-YOA@BQEp=N$#r@o1xz686U;J z3qj2&d>+c>Z%BgV+UTQpM0Y`z^X?zBTn0E{`>t>H@a@A;sWfiS=t`hlS@S;e-uqdQ z0(#e>TtoWU3e~yRb$9wPr#(`;;Wid1TQl-Ue5i>eV!IJOtra}m^-z9a+g|uFYAcg- zjbV-AvkJeeJfK7N5ka1X5HI+CXTl zbJE@zD1?k#j03w=s4jl9osLH(G|lAt2(RA^Wxt6Vv2v_^WS=X)J9@wSS9i3fwc>ED z!BM}!Z%cZ1$;t6q#kU7s;yKTH{#7CdY2n{=x_b+(FHFM;_W=j^-tq&!0}m$gy0Qh- zC<9^P@ibGTlux1OgRS_*ahS^aW()1(H!?C@H2f;J{c-js?mbd0Aq7G#p_u z!R?H{^x9Y^B#kly-C_m86WO$DUT2gV8~)2liS0bZV_YR2IqEJ?S2we7u5CBnh(Wf> zMRFvp+Q#ld&W?1sWh>r0Bw$+@(RQ-F=UrtoOS)_YD% zltS~1->O&20%wxG`Ev=%!Yr8Fw^#0`-JXRm8O@F@f<+}I^1$4*(6Qj98tgk~=Mhgz z)AhDS&LjN8o-S88beoJmV1~RT)~~>PT&AA5ehfk8Lm9pf zTRR|edNa$QdGBVuD3?lfw%3jsb}(;$TEcaNuGqGMqn!8E@Rh?sSBd8v?zN^*;^xl3LypXr6`yn&P068dUN z)6Sm{9~hVj2A&eg)Ed|Z$-Te-_L*{OaPk|ycy=*&p@JZgf5==Pl^eZ8A&JCLQk`7Z zy&tP3Y?JVlClP779THjSxchI<;y=}X7$00Kr(-uUg_Y0noA7v+_VyR7ei2@*tz{n`i0q|-ShB_KqN21or;Dr`C_9-+Tyx(Dab_fo}!B=cXs9hM?FQm9Qx z4?%aCRQDDi4LHJ@AIHwesXU0^XiQcQkzbZu@EOE1T;O2;Pa~Q?y`%SQzgIa8r_V-_ z5%>`{PhSLj2Kma=dzawpzY(RJdX7ywO33iGoj4Rc{94jQwqGQ#!hG}{796oSXqMHZ zT%MqLVZ4yXO4(KoWC!!3YR@eHGY$Xeh5p~>b3H4pUV>vl=Ru@P0;B?UK}}>MhP{lP_U_oMHA) z8s)E=>(9CQzo+OgkpCAjl6#bI=r-t=;HBCl)v@#!Fh_nN>fHc)wPg5`3fxOm;QMF@fzO1!QR68LA|qq+|0&NbIC1` zXtvJ~E)8gt;l&6Jp?@xN^=H4fX>`o}AY138SjQ;`;mueUx%S)w*U}mn$`PW`Nz~?; zosZ{rMYf*=R+|O+HNdhT$J41Qx*vEVT$Ea%wll@dP+~+O^L--SbC&*pxwL;?s7#>f z;JwifO!xOwbvGC}Rc-CdZBHMNTEE$(OxC(7$MI)RF&g{VT#+xpq&iA$ls-&CM#N(Y z%f>1@B3PG}=F{Zdk%C^1fVFzqn|*6od5qzuhW0_hr}_|J68OI86|v9&SxQB5&%Jfr zAAQt1Q%7E@+$A{EsH3`*{Ya4|SptJ!lGOWsAhXv^mqWJH|9<`d{EUAdE;Gzw2jcH5 zpr7}?_7@l4LGumH8Lb@1F7oq8TNauIX>5I^9(AvUkrS7KM@EVvX0z>Ays0!_W$uCpEJWj z&&A*a@%^{?AA7G@@SgI#g3rm=+0o-^>k=&k<)y-KA}b~cM!CrHUvwLqp$DvKoFMgBMU3&vnaq4VrvrxD%Qzj&%!>C5(5!u)*dWh3OU;(AHB`RZEOA(4| zs%2?y8dI1bhU$S%YX(v$pd>OFwARj6>QdR1DIG0we4LdrZHh!;-g!hkWLus|)W%`Z zZ;m8ZZLVb2X;qnH{_C9nzjylY;su)$vkq9&Ord;+Q4LZErGjGIl?sILxCOp-g1CzQEj7ypY<&;QPOp-8anOM<$OZavn@tO8Ozz%0K zrDRF^Wr{X${{te3b18cNB>{a?NOZHb@TNepjJI|oF%tdn7to`eEDnb|S9ixV^u-O8 zUB`FN89d1)CYYVHi^r%?r_DEc1q*-oQnFOw#XeU=kwdXG8@2!)#cniAZ_hyl3pbxr zVsOdF-E=QA^xW5}zcbVt{**UkJL;MS+fw46ZcA?4zlGx{wfZP%&Gx2Rze7n)T|Gc| z<2JMHVtbK&VobS0dyE2+jF~NxfM!Uef{>LiKHVendsYj%dN$2}e7s-zQDdoyJb(Ox ziaaRGbRs%zlZHtu)&`$tgA*_9{bNR;u!$5hDs2hqMoZ1X+p58&sQQiB`yG|L`3TaN z(<;@3c}`rRmLHZjOhVDEju5iMjW1rmZo)bY`029Wjh@$BmTOHg5DkZ8lLJP0@Zv*X z!@md6h60;ElgI0RbOODSFT@+eX>nx5Bf&p>m^VO}Ojqyh5|WcphUM`Khey$(7t&H# zKRvE6P_ZJ%v4Nht)yKU&GBj8yIO^TQ@4IYEoHNpY%Vti);FE^+N_;n|^dZ*L-@jWW zQjYyRiX|#8_5|-nU(E74wJs{ep%M8eP{jywsAtp4B{MS&Y1Xg;Bu)wj=uU@L!Amh3 zup9g-+%%-ng?>E)K)LlktBC({?8{Q-Uih~9%K&S;pN~H$xrK|8NQ{+k`$<6^gX!as zDU1tY=AzDNZLV=aE;u8Tn=I|#q35t^C2vPSj^8EB8>Cn7cP8F)&VDBg-SAFIb>A6@C0 zX(ECW^@uYlAt$SpMc1fBJFVBE2UDo1vS)piE=+i_IHRv)n|xn@s$(C9d-R)alJlh^ zBtOEqlmgmffpSy&59f;R;A7=+wR?PVt6pfS3bk-TfW2mI3Z{?dHejmk@BL@pthZ_! zM^TZ{YJ(=r$5NfQuA2)>t5r=nv|Y3%bLEf+*Kam_8875hyjh5N?GiaB_=GdK8_EOH z?ubzbtv<{YD}wK=N z;~!eQRYH_;3a5PfRH0FwS){DrVwVJD0%%rSI_;7AqI*NQI^BW494+ssEFd8zoS zO}rN8jmPe;hzhUMEX?K$_x>@4y?4V>K^H@QGrccD!6(htqq3kb77Za{&d%}RG{vk6vE|gbf-Xt?@)6KTSobk4)g?eqh=CI3kNa8R zy)!7FfqDFhcB~a#jqycU{Xo_>$AhmqPDX1vDJ#sEGYdi~6<33ts@&gcw~P$gU7;^m zC@KD6V<~3}DNfhz$EJ}(N+`;$gk5PvVJAHSx_QV4FIXT~BkG)xr?Vk4aBPF=d!&19 z%+J7zY;$1Jib9RhceAy04eUW}N81ioGTgpRP{wsW&!@U5c^4f&Gf_@wyJW<&!_y<` zbGtFbOg07x?M2pCTbzbTF=Z4I!E1XizV~}{R$wDkD;F!Gw%z!#?dh?a*V)L7R^#rs zUzUT1%PeN&`w!S@Kf?l5O{_MohAHser zJ&Co9tRDyUuO)u=4oP-;q@Z{wOuVBahhoSrIFwkit})S%SW*xzYQwwk%!)=I*Fg2#*vDoS2E~ znZ9W)-G@FEc)apw+#bH%9-1ILfKD3+s<;3^dVR- z&h0!I&Qm8{VSI6CA2*KEJo0V_pDwPpD6FMHY;U!s=kl1BJQcEq$q_;v?`}f0>RJ|0 zgE5-_i2(3!Mt*f47rUmxH{K389sZzvn}kc&)gt<_hud(?>qC8xWEX4==58ITP@O}3 z)~MiZ>(89Zsh-YbMJ)`w922Kyuf~<{8FilHtM<4(a)(7{6ssV&J z0oP~C&y`!W3W+s6Myw~_b6Ea()`8eC`SzP#651y0s}F#*e>dm0@N&YZ%Tuj zi%?d>Sn&Mn9CAKdu~j7Y;E@jLchzo60wlPRM0}=RqDKso`{A17g--VfX2ZLtOozU+ zy4i&B9KK_7tJzwg>AMknpT}?bX-XUN#Y$XRd-mh8w8s}x#frY_O43y z+d?SYOa<3;C_8?P*7iK!$ukNL-Fm8TUZvsc@E!Z^`gsoyg*Cg9-ibUqX(3JBey zG?ymDf4|=xtLdw-NT|ftWLbODufjYi@$ z+kmmhtL>w<@Z7_83JLhL;{%c;t1!biVkdg>JFMAB} zP$_abh$x!*(K0N2BTT>3V#6AQiW_fr9P&^&Pb>e_-4#&YH*IbMw9ikP2+6GaziC&{ z?p3cMv3e9H1nHKf!VmB4x^5m3IeyxT0Y?TIByj7K+>L(wun&wMV@(OK8(>;4g{HmVpd z@H?sKvOfFY;6Tt29&5#mf0$PlIqv}}BOVjc?n+ud4RVZ{LISyI`~sfi28)Nye3>(i zb;j%s=Na~9eXVt-MHzOxSF#!&5P-qDhg?*% zf`=uO_s6whMvAiQoA=jua@nwre*MT>! z7`Hx$dv#p4(+f8)1L{-|!5aeJN@L|eDmt;}clM1Fwa&WQrGq?avXq-uw>mGV#@-rW z54`y$@yN-#R_s>gZOv45aTsp7j^YTOGPQP-cD2Cszwo{{EWBB{^yrh|uBCo?4$bVl`qu~? zvq)%@nE3f5C`IC;2!bUf`37QC2(LsXYJ*6Y$dyDKS@)e@%<$W;r;RMD%|Mf&Kd?CD z=Q&nlYhPFibbsIooXJq4Pg4HKyj&zPRRU_i{GBz(E{p`(QR?uNAa_X{=; z?BH$*57D3GHdSq9PTD=-_q(yn+ioqJk+!Nx%Yt^tONNAkL@gMk+g@NBu)dc-hn`|kT!|$$mhK0T5;3TU;fg|*~&Tfh5o z^=1wAz|r38bV~a8sh4(dE0m0OV8?iN=^S60yZLV~ipzy0JF z9}bIgA>Rqr=FcP8%DbZerd!7_(pZ379)Z7EC86XuA_&nlSX}3KuTd+Dzi|NrignSw zsx-2XXF9K-a=Bn%5YJchY{9A;7>l9dW81OO;hv0tO_$-|^mX|sVt42CmEU)WM)Pi+ z3@fzje##;b1|Dl35mc^(a)9#Ok3iTA?fzu}0r=z;L}LEQ{yp}`l{cvC1r^n8Yd0>b zFeUKG+RazsCtK1VHrRJPI9F}&^{ynxqx?zf9Gc9k$vA)7)4V!YPvsplWNy+ULaME# z<7K|`c|An%V3EZyn!9(litH`@Z0BnfZ*b+%cE6w zIbsd*_I6J~KZMTQxgxa{=qjPl&=J@<2nB2yLR4fu$Cw>C{=+l!8d`nR+LS>TVz=wu zaMLd#vRuX=DH;Ti%JbB)NJpStzg{GnbMmQ1Ic`LKEtaXK)!?q%gsA#t6mywfd zZ7GIxsl&Kq1GuJCX|xab~xaRA3gB9VzWX za|qXy;~jvqR>S9uSlvYkOOAyl)VMU+qA^TdOt;E~Yp^BI{vmKe^b2M7;LpdlP-nI) z(9$|YS{=h1-!vIAb`-Sh=ctHk*)CAlpec84ezyy!ixyW31^ENo5-u9YdcxN??Pcx&U`8^TCVPYa4`hNKn@IxSA z(|St#0B(5?Nq5zT)At|By0ky?k-xQs4doDTgibQ`Rp1}DKJZvMiZSty>~GMuo_x=u2_f@&Ggpr!IYbfN ziiEO^GZI5{8!sy_f1_V8X&R*5|MIFxKgMXfW2edob;j^!Wrr>(Ba;)7dr9(EC7DJ4 zSetNV^6}0=HQ(p7Ifq4=218&t29l=w;a(0sn_DLTu9ER{UmSrA!)RG8cAn9y0C>mn zW|&*scyGvkD9g|+Na zzX$xaeP?%9`@!10qR)U=YhO*axSy?gWvrNvEO)TKrKL&2a;2-n<#Y@fd7NZubbMw0I za2_lbsegWfcw<&j{No;Qh8VMeCBHAvte-~j{uvv&pLAi|KjAhXP5LhNOkwd0u2>hH z)pYJYWL7yd(SScNcdaC^0!75DEs8G3-!msFuV|{w zqCKx`<3nM!Kuqh1%Y0+OFitNED^gXx`|m||@L}EDy<2AehjPs%-j$!c!t~@VgSS8R zIlI*Nqe;XkA~Hz$zn8zsW(h*YKVPS4HY)czgH%_kM$#<;MZg69c{*+kTkBkG`@YI@ z!{px;vq=pFW|VI(XWoe2M@s5pRLqH#sZUzWmgz#b2!XOq#bF01$Sfu3M-rHMCD+{4 zF&+l#(y5Iq>~WSQlvj;;CPQ;Zu?BghJg*T*GF`L1HZfVO6I$geuem{K*K=CgXc6tU z3JJ@&$KPo@1k2 z{4vy)i$d4PWe>`Wn=DVl&N*2X3q4P!1m}8gR3Uta*j3DY4Q7}&m1`&FFo_^OSYp}< z&D&vfB9SG(7d`fHvT%2~4=hbrK*-19qHkdFJ0d_NZ9Vv*twEJ+g><-u)acAaYvgmr z58a%>fB+865de~=@Ccu*hcIzu^{KTwm@3zcBB*GUz5jN)3++y-QCWjJ>tcDobF;wt=)xG);`S3IQ+DU#8M3M|5nR zTt8uBrtxx5<8E8!%ei3yot+JWgi~3LEB;RwfCb4iy^bo@iulVkwZ$qmvF_VVrk3|q zlBKnj6rTxac>YYt%-VZJ_vo~_R8k>zDq2X>O~#T&5pu=8 z+sWK%i4mAouKJIe7&iV zih%4EFa@5VfnJu>c*|XP4D3vx4C>r4QfuShcyapE-tOtI{Q24Vb%i|ZkaF}Tx9yS$br^$S}-N^_1N=q=!uYYb%ubMM((xwErsdW?5Bw7`_XhiKv^H+L}?d3pxj#JDI>J&c&+v| zPvn-^!czwe3T>vW_x`M!=4DI4&$ShL99y*D%jP6viS8gD34NYa=>Yf2zR}*nhfo2) zXgT0+k$X4j{mJmWL<}bDgK`k!+_tgIAEs6YG*7Mk-o|PAOO5?(3v&b{Kdv+e8=tkd zM;2)3qCN6Fkkm6)4w2t>*ukNROH@`&DjHu8K=C&DhAtf}jkA`yGm)cAXmRT+8seMi z`*79b^m|!-E%UBD(i8gPDVe0CqKtqVt->CNoFd|Jx2{s}L{D&_Rt4#>2Y{l0Gnjg& zSmC@v?(_9qF?o#~!mDy?QLT4%Dd&k#a*V}D_MFgO~7XXqOs+S#$6NT;C_ z2+E|!zeEeoh%WZdCx9Hn)ho@Tqz$COv^7j?m&mW;?NahOFZM?p2 zIac8Z+A1QBbgB%`rJkgP9z$W3HSu|FuP`bggF3sY-e}k{m$>hdN;-hDuNAOzUX-&p z=aZahWl;i;{vr6Dn>t?3AtLp+U%{HEP=cCz%=tvvuM2-o9*>hU)~l}mwO1vRL_9Xb zff$cA;x{_S(d_3P{WoH;;m-lm(HAQQS4VD-k27bxH@z`*gKi8{J4Du;lKG6)e`k!C zARP)La6B4wmO<25{m>z}j}-AwlRoh^j(0@rZ!{>8_f1~s2O7I^iDw$?7EZyxId6jB zRhx#ca5;Ud|*<48cs!`t!1~HQE6@eZ5ndpD0}25uEQ^GVjZo#? zI=~mgJ`FFN3FmTv@H_sFO!7(&L|AVRdOSA8#Qy=by zUpoHu{bh0k4Wj)WHu#6nFb42{VPY65Ej+ zP{@o@5Ja-2ih*F!-S_vL;K(dH2{743qIm1Ia5R1N(@5>2P;Y!^jH2GB-=pnt`sd>% zA=fEyzia#jTc>2R;O6t4YK&Fi7+>`tJPeu1+4JXcykTn_%_GFD1*<$1tMQm&?2qD; zpb{wbxHm;Py!D9B4UK5u_5tv~`ee+-jngR=&<_bpp#Kt0loh(ha7~ySBJ-xVHq89E z37=ml*uM`_N!iM3Hu$WS?HgOI`sh&#x^&^C8$Y@9oDlx=e;$->i)W;LxG5 zvxn%Y=EZc~UEe4rb!GRP*}P~BYKJ@jt%~g?Iw5Ux&2#=O2wm&t27*U8z+I&O zy2bOA9R{}SuuMcE@iWsZ5@izpY*mHt5ik3qpFP4;6_Pb$mNZ1_BWedOdCc1MVw}Q2 zV!7w0u*ou|kztyzdEK%U>?pRm^@U&)DTmJF$Gglm;SSGu`@zPMHV(YIi(J^Fqc}xs zJz5kl-K7}Knhu|pWh~W6wwFgC9fp3h<#EgQu6GL2?Mw|@kHL1P$_aSj`vV*l_q9j~ zE3ezR?p@~IEClb#W`px!rXl1;lgsV+{`8^XsxHLIG;rdXbg#wFvYqI2R>%pcYkgrl zh3LcOO`RYC>+K;`jr5IOw=zq5)> zI@i{$NY#t!q;>ASIS!YAyGqJ0yQ)tVy&RjB zslOV_bCt7ll`4Ho(JO*JdyZ--iblZCL0h%{K{Sg5@O z=LYtqZhnghLaxxEZ3RuD-NAQk6lXxZ+=LtxzFs%!Tx~%l?j34GYb9}x9fgfV>WRNM zx8HdPd78O5I@?7DC7GOcJH>r4)f}l@bP`4`H-oF#1uRis3zNMY$2%W$`lHOK`u0

NEzKA7bB&3Di`_jy)_{{RYcud5ry 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≻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 literal 0 HcmV?d00001 diff --git a/examples/quotes/src/App.tsx b/examples/quotes/src/App.tsx new file mode 100644 index 000000000..5f886ac05 --- /dev/null +++ b/examples/quotes/src/App.tsx @@ -0,0 +1,172 @@ +import React, { useRef, useState, useEffect } from 'react'; +import Container from 'react-bootstrap/Container'; +import { NavLink } from 'react-router'; +import Row from 'react-bootstrap/Row'; +import Col from 'react-bootstrap/Col'; +import Form from 'react-bootstrap/Form'; +import Button from 'react-bootstrap/Button'; +import Spinner from 'react-bootstrap/Spinner'; +import Stack from 'react-bootstrap/Stack'; +import CloseButton from 'react-bootstrap/CloseButton'; +import ToggleButton from 'react-bootstrap/ToggleButton'; +import Pagination from 'react-bootstrap/Pagination'; +import type { Quote, Tag } from './models'; + +export default function App() { + const inputRef = useRef(null); + const [knn, setKnn] = useState(true); + const [query, setQuery] = useState(''); + const [filters, setFilters] = useState([]); + const [tags, setTags] = useState([]); + const [results, setResults] = useState(null); + const [start, setStart] = useState(0); + const [total, setTotal] = useState(0); + + useEffect(() => { + if (inputRef.current) { + inputRef.current.value = query; + } + }, [query]); + + const onSearch = (ev: React.FormEvent) => { + ev.preventDefault(); + setQuery(inputRef.current?.value || ''); + setStart(0); + }; + + const onResetQuery = () => { + setQuery(''); + setStart(0); + setTags(null); + setResults(null); + inputRef.current?.focus(); + }; + + const onResetFilters = () => { + setFilters([]); + setStart(0); + setTags(null); + setResults(null); + inputRef.current?.focus(); + }; + + const onFilter = ({tag, count}: Tag) => { + setFilters([...filters, tag]); + setStart(0); + setTags(null); + setResults(null); + }; + + useEffect(() => { + (async () => { + const response = await fetch('http://github.com/api/search', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({query, filters, knn, start}), + }); + const data: {quotes: Quote[], tags: Tag[], start: number, total: number} = await response.json(); + setResults(data.quotes); + setTags(data.tags.filter(tag => !filters.includes(tag.tag))); + setStart(data.start); + setTotal(data.total); + window.scrollTo(0, 0); + })() + }, [query, filters, knn, start, total]); + + return ( + + +

Elasticsearch + Pydantic Demo

+
+ + + + setKnn(e.currentTarget.checked)}> + + + New Quote + +
+ + + + <> + {filters != null && ( + <> + {filters.map(tag => ( +
+ » {tag} +
+ ))} + {(filters.length > 0) && ( + <> + +
+ + )} + + )} + {(tags === null) ? + + Loading... + + : + <> + {(tags.length === 0) ? +

No tags.

+ : + <> + {tags.map(({tag, count}) => ( +
+ ({count}) +
+ ))} + + } + + } + + + + {(results === null) ? + + Loading... + + : + <> + {(results.length === 0) ? +

No results. Sorry!

+ : + <> +

Showing results {start + 1}-{start + results.length} of {total}.

+ {results.map(({quote, author, tags, meta}, index) => ( +
+

+ {quote}{author} +
+ [Score: {meta.score}] {tags.map(tag => `#${tag}`).join(', ')} +
+ Edit +

+
+ ))} + + } + + {start > 0 && + <> + setStart(0)} /> + setStart(start - results.length)} /> + + } + {results.length > 0 && start + results.length < total && + setStart(start + results.length)} /> + } + + + } + +
+ + ); +} diff --git a/examples/quotes/src/Quote.tsx b/examples/quotes/src/Quote.tsx new file mode 100644 index 000000000..6cde7a440 --- /dev/null +++ b/examples/quotes/src/Quote.tsx @@ -0,0 +1,104 @@ +import { useState, useEffect, useRef } from 'react'; +import type { MouseEvent } from 'react'; +import { useParams, NavLink, useNavigate } from 'react-router'; +import Container from 'react-bootstrap/Container'; +import Stack from 'react-bootstrap/Stack'; +import Form from 'react-bootstrap/Form'; +import Button from 'react-bootstrap/Button'; +import Toast from 'react-bootstrap/Toast'; +import ToastContainer from 'react-bootstrap/ToastContainer'; +import type { Quote } from './models'; + +export default function Quote() { + const [quote, setQuote] = useState(undefined); + const [status, setStatus] = useState(""); + const params = useParams(); + const navigate = useNavigate(); + const quoteField = useRef(null); + const authorField = useRef(null); + const tagsField = useRef(null); + + useEffect(() => { + (async () => { + const response = await fetch(`/api/quotes/${params.id}`); + const data = await response.json(); + setQuote(data); + })(); + }, []); + + const onSubmit = async (ev: MouseEvent) => { + ev.preventDefault(); + if (quote) { + quote.quote = quoteField.current?.value ?? ''; + quote.author = authorField.current?.value ?? ''; + quote.tags = (tagsField.current?.value ?? "").split(','); + let url = '/api/quotes'; + let method = 'POST'; + if (params.id !== 'new') { + url += `/${params.id}`; + method = 'PUT'; + } + const response = await fetch(url, { + method: method, + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(quote), + }); + if (response.status == 200 || response.status == 201) { + const data = await response.json(); + navigate(`/quotes/${data.meta.id}`); + } + else { + setStatus('Save error'); + } + } + }; + + const onDelete = async (ev: MouseEvent) => { + ev.preventDefault(); + const response = await fetch(`/api/quotes/${params.id}`, { + method: 'DELETE', + }); + if (response.status == 204) { + navigate('/'); + } + else { + setStatus('Delete error'); + } + }; + + return ( + +

Elasticsearch + Pydantic Demo

+
+ + setStatus('')} show={status != ''} delay={3000} autohide> + {status} + + + + Quote + + + + Author + + + + Tags + + + + + {params.id !== 'new' && + + } + Back + +
+
+ ); +} diff --git a/examples/quotes/src/index.css b/examples/quotes/src/index.css new file mode 100644 index 000000000..6ac8038d5 --- /dev/null +++ b/examples/quotes/src/index.css @@ -0,0 +1,55 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} + +.SearchForm { + margin-bottom: 25px; +} + +.Tags { + margin-top: 10px; +} + +.Results { + margin-top: 10px; +} + +.Results .Summary { + font-size: 0.9em; + font-style: italic; +} + +.Filter { + font-style: italic; +} + +.Tags button { + padding: 0px; +} + +.ResultQuote { + font-weight: bold; +} + +.ResultAuthor { +} + +.ResultScore { + font-size: 0.8em; +} + +.ResultTags { + font-size: 0.8em; + font-style: italic; +} + diff --git a/examples/quotes/src/main.tsx b/examples/quotes/src/main.tsx new file mode 100644 index 000000000..404bbe7d9 --- /dev/null +++ b/examples/quotes/src/main.tsx @@ -0,0 +1,24 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import { createBrowserRouter, RouterProvider } from 'react-router'; +import 'bootstrap/dist/css/bootstrap.min.css'; +import './index.css' +import App from './App.tsx' +import Quote from './Quote.tsx' + +const router = createBrowserRouter([ + { + path: '/', + element: , + }, + { + path: '/quotes/:id', + element: , + }, +]); + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/examples/quotes/src/models.tsx b/examples/quotes/src/models.tsx new file mode 100644 index 000000000..e426b705c --- /dev/null +++ b/examples/quotes/src/models.tsx @@ -0,0 +1,16 @@ +export interface Meta { + id: string; + score: number; +} + +export interface Quote { + meta: Meta; + quote: string; + author: string; + tags: string[]; +}; + +export interface Tag { + tag: string; + count: number; +}; diff --git a/examples/quotes/tsconfig.app.json b/examples/quotes/tsconfig.app.json new file mode 100644 index 000000000..a9b5a59ca --- /dev/null +++ b/examples/quotes/tsconfig.app.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/examples/quotes/tsconfig.json b/examples/quotes/tsconfig.json new file mode 100644 index 000000000..1ffef600d --- /dev/null +++ b/examples/quotes/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/examples/quotes/tsconfig.node.json b/examples/quotes/tsconfig.node.json new file mode 100644 index 000000000..7a77bab3b --- /dev/null +++ b/examples/quotes/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": [], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/quotes/vite.config.ts b/examples/quotes/vite.config.ts new file mode 100644 index 000000000..c3c29c6b0 --- /dev/null +++ b/examples/quotes/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], + server: { + proxy: { + '/api': 'http://localhost:5000/', + '/docs': 'http://localhost:5000/', + '/redoc': 'http://localhost:5000/', + '/openapi.json': 'http://localhost:5000/', + }, + }, +}) diff --git a/noxfile.py b/noxfile.py index 73f3df3d5..ca7f028d2 100644 --- a/noxfile.py +++ b/noxfile.py @@ -90,6 +90,7 @@ def lint(session): "flake8", "black~=25.0", "mypy", + "pydantic", "isort~=6.0", "types-requests", "types-python-dateutil", diff --git a/pyproject.toml b/pyproject.toml index fe0a9ca6c..c12a5a9df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,6 +67,7 @@ dev = [ "python-dateutil", "unasync", "pyyaml>=5.4", + "pydantic", "isort", "black", "twine", @@ -139,4 +140,5 @@ exclude_lines = [ ] [tool.mypy] +plugins = ["pydantic.mypy"] ignore_missing_imports = true diff --git a/test_elasticsearch/test_dsl/_async/test_document.py b/test_elasticsearch/test_dsl/_async/test_document.py index 8ac0fc887..c986e588e 100644 --- a/test_elasticsearch/test_dsl/_async/test_document.py +++ b/test_elasticsearch/test_dsl/_async/test_document.py @@ -27,8 +27,9 @@ import sys from datetime import datetime from hashlib import md5 -from typing import Any, ClassVar, Dict, List, Optional +from typing import Annotated, Any, ClassVar, Dict, List, Optional +import pydantic import pytest from pytest import raises @@ -36,6 +37,7 @@ AsyncDocument, Index, InnerDoc, + Keyword, M, Mapping, MetaField, @@ -47,6 +49,7 @@ ) from elasticsearch.dsl.document_base import InstrumentedField from elasticsearch.dsl.exceptions import IllegalOperation, ValidationException +from elasticsearch.dsl.pydantic import AsyncBaseESModel class MyInner(InnerDoc): @@ -678,118 +681,150 @@ class TypedDoc(AsyncDocument): ) i1: ClassVar i2: ClassVar[int] - - props = TypedDoc._doc_type.mapping.to_dict()["properties"] - assert props == { - "st": {"type": "text"}, - "dt": {"type": "date"}, - "li": {"type": "integer"}, - "ob": { - "type": "object", - "properties": { - "st": {"type": "text"}, - "dt": {"type": "date"}, - "li": {"type": "integer"}, + i3: str = mapped_field(exclude=True) + + class TypedDocAnnotated(AsyncDocument): + st: Annotated[str, "foo"] + dt: Annotated[Optional[datetime], "bar"] + li: Annotated[List[int], "baz"] + ob: Annotated[TypedInnerDoc, "qux"] + ns: Annotated[List[TypedInnerDoc], "quux"] + ip: Annotated[Optional[str], field.Ip()] + k1: Annotated[str, field.Keyword(required=True)] + k2: Annotated[M[str], field.Keyword()] + k3: Annotated[str, mapped_field(field.Keyword(), default="foo")] + k4: Annotated[M[Optional[str]], mapped_field(field.Keyword())] # type: ignore[misc] + s1: Annotated[Secret, SecretField()] + s2: Annotated[M[Secret], SecretField()] + s3: Annotated[Secret, mapped_field(SecretField())] # type: ignore[misc] + s4: Annotated[ + M[Optional[Secret]], + mapped_field(SecretField(), default_factory=lambda: "foo"), + ] + if sys.version_info[0] > 3 or ( + sys.version_info[0] == 3 and sys.version_info[1] >= 11 + ): + i1: Annotated[ClassVar, "classvar"] + i2: Annotated[ClassVar[int], "classvar"] + else: + i1: ClassVar + i2: ClassVar[int] + i3: Annotated[str, mapped_field(exclude=True)] + + for doc_class in [TypedDoc, TypedDocAnnotated]: + props = doc_class._doc_type.mapping.to_dict()["properties"] + assert props == { + "st": {"type": "text"}, + "dt": {"type": "date"}, + "li": {"type": "integer"}, + "ob": { + "type": "object", + "properties": { + "st": {"type": "text"}, + "dt": {"type": "date"}, + "li": {"type": "integer"}, + }, }, - }, - "ns": { - "type": "nested", - "properties": { - "st": {"type": "text"}, - "dt": {"type": "date"}, - "li": {"type": "integer"}, + "ns": { + "type": "nested", + "properties": { + "st": {"type": "text"}, + "dt": {"type": "date"}, + "li": {"type": "integer"}, + }, }, - }, - "ip": {"type": "ip"}, - "k1": {"type": "keyword"}, - "k2": {"type": "keyword"}, - "k3": {"type": "keyword"}, - "k4": {"type": "keyword"}, - "s1": {"type": "text"}, - "s2": {"type": "text"}, - "s3": {"type": "text"}, - "s4": {"type": "text"}, - } + "ip": {"type": "ip"}, + "k1": {"type": "keyword"}, + "k2": {"type": "keyword"}, + "k3": {"type": "keyword"}, + "k4": {"type": "keyword"}, + "s1": {"type": "text"}, + "s2": {"type": "text"}, + "s3": {"type": "text"}, + "s4": {"type": "text"}, + } - TypedDoc.i1 = "foo" - TypedDoc.i2 = 123 + doc_class.i1 = "foo" + doc_class.i2 = 123 + doc_class.i3 = "bar" + + doc = doc_class() + assert doc.k3 == "foo" + assert doc.s4 == "foo" + with raises(ValidationException) as exc_info: + doc.full_clean() + assert set(exc_info.value.args[0].keys()) == { + "st", + "k1", + "k2", + "ob", + "s1", + "s2", + "s3", + } - doc = TypedDoc() - assert doc.k3 == "foo" - assert doc.s4 == "foo" - with raises(ValidationException) as exc_info: + assert doc_class.i1 == "foo" + assert doc_class.i2 == 123 + assert doc_class.i3 == "bar" + + doc.st = "s" + doc.li = [1, 2, 3] + doc.k1 = "k1" + doc.k2 = "k2" + doc.ob.st = "s" + doc.ob.li = [1] + doc.s1 = "s1" + doc.s2 = "s2" + doc.s3 = "s3" doc.full_clean() - assert set(exc_info.value.args[0].keys()) == { - "st", - "k1", - "k2", - "ob", - "s1", - "s2", - "s3", - } - assert TypedDoc.i1 == "foo" - assert TypedDoc.i2 == 123 - - doc.st = "s" - doc.li = [1, 2, 3] - doc.k1 = "k1" - doc.k2 = "k2" - doc.ob.st = "s" - doc.ob.li = [1] - doc.s1 = "s1" - doc.s2 = "s2" - doc.s3 = "s3" - doc.full_clean() + doc.ob = TypedInnerDoc(li=[1]) + with raises(ValidationException) as exc_info: + doc.full_clean() + assert set(exc_info.value.args[0].keys()) == {"ob"} + assert set(exc_info.value.args[0]["ob"][0].args[0].keys()) == {"st"} - doc.ob = TypedInnerDoc(li=[1]) - with raises(ValidationException) as exc_info: - doc.full_clean() - assert set(exc_info.value.args[0].keys()) == {"ob"} - assert set(exc_info.value.args[0]["ob"][0].args[0].keys()) == {"st"} + doc.ob.st = "s" + doc.ns.append(TypedInnerDoc(li=[1, 2])) + with raises(ValidationException) as exc_info: + doc.full_clean() - doc.ob.st = "s" - doc.ns.append(TypedInnerDoc(li=[1, 2])) - with raises(ValidationException) as exc_info: + doc.ns[0].st = "s" doc.full_clean() - doc.ns[0].st = "s" - doc.full_clean() - - doc.ip = "1.2.3.4" - n = datetime.now() - doc.dt = n - assert doc.to_dict() == { - "st": "s", - "li": [1, 2, 3], - "dt": n, - "ob": { + doc.ip = "1.2.3.4" + n = datetime.now() + doc.dt = n + assert doc.to_dict() == { "st": "s", - "li": [1], - }, - "ns": [ - { + "li": [1, 2, 3], + "dt": n, + "ob": { "st": "s", - "li": [1, 2], - } - ], - "ip": "1.2.3.4", - "k1": "k1", - "k2": "k2", - "k3": "foo", - "s1": "s1", - "s2": "s2", - "s3": "s3", - "s4": "foo", - } + "li": [1], + }, + "ns": [ + { + "st": "s", + "li": [1, 2], + } + ], + "ip": "1.2.3.4", + "k1": "k1", + "k2": "k2", + "k3": "foo", + "s1": "s1", + "s2": "s2", + "s3": "s3", + "s4": "foo", + } - s = TypedDoc.search().sort(TypedDoc.st, -TypedDoc.dt, +TypedDoc.ob.st) - s.aggs.bucket("terms_agg", "terms", field=TypedDoc.k1) - assert s.to_dict() == { - "aggs": {"terms_agg": {"terms": {"field": "k1"}}}, - "sort": ["st", {"dt": {"order": "desc"}}, "ob.st"], - } + s = doc_class.search().sort(doc_class.st, -doc_class.dt, +doc_class.ob.st) + s.aggs.bucket("terms_agg", "terms", field=doc_class.k1) + assert s.to_dict() == { + "aggs": {"terms_agg": {"terms": {"field": "k1"}}}, + "sort": ["st", {"dt": {"order": "desc"}}, "ob.st"], + } @pytest.mark.skipif(sys.version_info < (3, 10), reason="requires Python 3.10") @@ -881,3 +916,107 @@ class Doc(AsyncDocument): Doc.ns.something with raises(AttributeError): Doc.ns.st.something + + +def test_pydantic_integration() -> None: + class Location(pydantic.BaseModel): + city: str + country: str + + class Address(pydantic.BaseModel): + street: str + location: Location + + class Phone(pydantic.BaseModel): + type: str + number: str + + class User(AsyncBaseESModel): + name: Annotated[str, Keyword()] = pydantic.Field(default="Unknown") + bio: str + age: int = pydantic.Field(strict=True, ge=0) + address: Address = pydantic.Field( + default=Address( + street="Unknown", location=Location(city="Unknown", country="Unknown") + ) + ) + phones: list[Phone] = pydantic.Field(default=[]) + + u = User( + name="foo", + bio="lorem ipsum", + age=30, + address=Address( + street="123 Main St.", + location=Location(city="San Francisco", country="USA"), + ), + ) + u.phones.append(Phone(type="Home", number="+12345678900")) + assert u.model_dump() == { + "name": "foo", + "bio": "lorem ipsum", + "age": 30, + "address": { + "street": "123 Main St.", + "location": { + "city": "San Francisco", + "country": "USA", + }, + }, + "phones": [ + { + "type": "Home", + "number": "+12345678900", + } + ], + "meta": { + "id": "", + "index": "", + "primary_term": 0, + "score": 0, + "seq_no": 0, + "version": 0, + }, + } + udoc = u.to_doc() + assert udoc.name == "foo" + assert udoc.bio == "lorem ipsum" + assert udoc.age == 30 + assert udoc.to_dict() == { + "name": "foo", + "bio": "lorem ipsum", + "age": 30, + "address": { + "street": "123 Main St.", + "location": { + "city": "San Francisco", + "country": "USA", + }, + }, + "phones": [ + { + "type": "Home", + "number": "+12345678900", + } + ], + } + + u2 = User.from_doc(udoc) + assert u == u2 + + with raises(pydantic.ValidationError): + _ = User() + + with raises(pydantic.ValidationError): + _ = User(name="foo", bio="lorem ipsum") + + with raises(pydantic.ValidationError): + _ = User(name="foo", bio="lorem ipsum", age=-10) + + u3 = User(bio="lorem ipsum", age=30) + assert u3.name == "Unknown" + assert u3.address.location.country == "Unknown" + assert u3.phones == [] + assert u3.to_doc().name == "Unknown" + assert u3.to_doc().address.location.country == "Unknown" + assert u3.to_doc().phones == [] diff --git a/test_elasticsearch/test_dsl/_sync/test_document.py b/test_elasticsearch/test_dsl/_sync/test_document.py index 05ad9d623..c4e0cc31a 100644 --- a/test_elasticsearch/test_dsl/_sync/test_document.py +++ b/test_elasticsearch/test_dsl/_sync/test_document.py @@ -27,8 +27,9 @@ import sys from datetime import datetime from hashlib import md5 -from typing import Any, ClassVar, Dict, List, Optional +from typing import Annotated, Any, ClassVar, Dict, List, Optional +import pydantic import pytest from pytest import raises @@ -36,6 +37,7 @@ Document, Index, InnerDoc, + Keyword, M, Mapping, MetaField, @@ -47,6 +49,7 @@ ) from elasticsearch.dsl.document_base import InstrumentedField from elasticsearch.dsl.exceptions import IllegalOperation, ValidationException +from elasticsearch.dsl.pydantic import BaseESModel class MyInner(InnerDoc): @@ -678,118 +681,150 @@ class TypedDoc(Document): ) i1: ClassVar i2: ClassVar[int] - - props = TypedDoc._doc_type.mapping.to_dict()["properties"] - assert props == { - "st": {"type": "text"}, - "dt": {"type": "date"}, - "li": {"type": "integer"}, - "ob": { - "type": "object", - "properties": { - "st": {"type": "text"}, - "dt": {"type": "date"}, - "li": {"type": "integer"}, + i3: str = mapped_field(exclude=True) + + class TypedDocAnnotated(Document): + st: Annotated[str, "foo"] + dt: Annotated[Optional[datetime], "bar"] + li: Annotated[List[int], "baz"] + ob: Annotated[TypedInnerDoc, "qux"] + ns: Annotated[List[TypedInnerDoc], "quux"] + ip: Annotated[Optional[str], field.Ip()] + k1: Annotated[str, field.Keyword(required=True)] + k2: Annotated[M[str], field.Keyword()] + k3: Annotated[str, mapped_field(field.Keyword(), default="foo")] + k4: Annotated[M[Optional[str]], mapped_field(field.Keyword())] # type: ignore[misc] + s1: Annotated[Secret, SecretField()] + s2: Annotated[M[Secret], SecretField()] + s3: Annotated[Secret, mapped_field(SecretField())] # type: ignore[misc] + s4: Annotated[ + M[Optional[Secret]], + mapped_field(SecretField(), default_factory=lambda: "foo"), + ] + if sys.version_info[0] > 3 or ( + sys.version_info[0] == 3 and sys.version_info[1] >= 11 + ): + i1: Annotated[ClassVar, "classvar"] + i2: Annotated[ClassVar[int], "classvar"] + else: + i1: ClassVar + i2: ClassVar[int] + i3: Annotated[str, mapped_field(exclude=True)] + + for doc_class in [TypedDoc, TypedDocAnnotated]: + props = doc_class._doc_type.mapping.to_dict()["properties"] + assert props == { + "st": {"type": "text"}, + "dt": {"type": "date"}, + "li": {"type": "integer"}, + "ob": { + "type": "object", + "properties": { + "st": {"type": "text"}, + "dt": {"type": "date"}, + "li": {"type": "integer"}, + }, }, - }, - "ns": { - "type": "nested", - "properties": { - "st": {"type": "text"}, - "dt": {"type": "date"}, - "li": {"type": "integer"}, + "ns": { + "type": "nested", + "properties": { + "st": {"type": "text"}, + "dt": {"type": "date"}, + "li": {"type": "integer"}, + }, }, - }, - "ip": {"type": "ip"}, - "k1": {"type": "keyword"}, - "k2": {"type": "keyword"}, - "k3": {"type": "keyword"}, - "k4": {"type": "keyword"}, - "s1": {"type": "text"}, - "s2": {"type": "text"}, - "s3": {"type": "text"}, - "s4": {"type": "text"}, - } + "ip": {"type": "ip"}, + "k1": {"type": "keyword"}, + "k2": {"type": "keyword"}, + "k3": {"type": "keyword"}, + "k4": {"type": "keyword"}, + "s1": {"type": "text"}, + "s2": {"type": "text"}, + "s3": {"type": "text"}, + "s4": {"type": "text"}, + } - TypedDoc.i1 = "foo" - TypedDoc.i2 = 123 + doc_class.i1 = "foo" + doc_class.i2 = 123 + doc_class.i3 = "bar" + + doc = doc_class() + assert doc.k3 == "foo" + assert doc.s4 == "foo" + with raises(ValidationException) as exc_info: + doc.full_clean() + assert set(exc_info.value.args[0].keys()) == { + "st", + "k1", + "k2", + "ob", + "s1", + "s2", + "s3", + } - doc = TypedDoc() - assert doc.k3 == "foo" - assert doc.s4 == "foo" - with raises(ValidationException) as exc_info: + assert doc_class.i1 == "foo" + assert doc_class.i2 == 123 + assert doc_class.i3 == "bar" + + doc.st = "s" + doc.li = [1, 2, 3] + doc.k1 = "k1" + doc.k2 = "k2" + doc.ob.st = "s" + doc.ob.li = [1] + doc.s1 = "s1" + doc.s2 = "s2" + doc.s3 = "s3" doc.full_clean() - assert set(exc_info.value.args[0].keys()) == { - "st", - "k1", - "k2", - "ob", - "s1", - "s2", - "s3", - } - assert TypedDoc.i1 == "foo" - assert TypedDoc.i2 == 123 - - doc.st = "s" - doc.li = [1, 2, 3] - doc.k1 = "k1" - doc.k2 = "k2" - doc.ob.st = "s" - doc.ob.li = [1] - doc.s1 = "s1" - doc.s2 = "s2" - doc.s3 = "s3" - doc.full_clean() + doc.ob = TypedInnerDoc(li=[1]) + with raises(ValidationException) as exc_info: + doc.full_clean() + assert set(exc_info.value.args[0].keys()) == {"ob"} + assert set(exc_info.value.args[0]["ob"][0].args[0].keys()) == {"st"} - doc.ob = TypedInnerDoc(li=[1]) - with raises(ValidationException) as exc_info: - doc.full_clean() - assert set(exc_info.value.args[0].keys()) == {"ob"} - assert set(exc_info.value.args[0]["ob"][0].args[0].keys()) == {"st"} + doc.ob.st = "s" + doc.ns.append(TypedInnerDoc(li=[1, 2])) + with raises(ValidationException) as exc_info: + doc.full_clean() - doc.ob.st = "s" - doc.ns.append(TypedInnerDoc(li=[1, 2])) - with raises(ValidationException) as exc_info: + doc.ns[0].st = "s" doc.full_clean() - doc.ns[0].st = "s" - doc.full_clean() - - doc.ip = "1.2.3.4" - n = datetime.now() - doc.dt = n - assert doc.to_dict() == { - "st": "s", - "li": [1, 2, 3], - "dt": n, - "ob": { + doc.ip = "1.2.3.4" + n = datetime.now() + doc.dt = n + assert doc.to_dict() == { "st": "s", - "li": [1], - }, - "ns": [ - { + "li": [1, 2, 3], + "dt": n, + "ob": { "st": "s", - "li": [1, 2], - } - ], - "ip": "1.2.3.4", - "k1": "k1", - "k2": "k2", - "k3": "foo", - "s1": "s1", - "s2": "s2", - "s3": "s3", - "s4": "foo", - } + "li": [1], + }, + "ns": [ + { + "st": "s", + "li": [1, 2], + } + ], + "ip": "1.2.3.4", + "k1": "k1", + "k2": "k2", + "k3": "foo", + "s1": "s1", + "s2": "s2", + "s3": "s3", + "s4": "foo", + } - s = TypedDoc.search().sort(TypedDoc.st, -TypedDoc.dt, +TypedDoc.ob.st) - s.aggs.bucket("terms_agg", "terms", field=TypedDoc.k1) - assert s.to_dict() == { - "aggs": {"terms_agg": {"terms": {"field": "k1"}}}, - "sort": ["st", {"dt": {"order": "desc"}}, "ob.st"], - } + s = doc_class.search().sort(doc_class.st, -doc_class.dt, +doc_class.ob.st) + s.aggs.bucket("terms_agg", "terms", field=doc_class.k1) + assert s.to_dict() == { + "aggs": {"terms_agg": {"terms": {"field": "k1"}}}, + "sort": ["st", {"dt": {"order": "desc"}}, "ob.st"], + } @pytest.mark.skipif(sys.version_info < (3, 10), reason="requires Python 3.10") @@ -881,3 +916,107 @@ class Doc(Document): Doc.ns.something with raises(AttributeError): Doc.ns.st.something + + +def test_pydantic_integration() -> None: + class Location(pydantic.BaseModel): + city: str + country: str + + class Address(pydantic.BaseModel): + street: str + location: Location + + class Phone(pydantic.BaseModel): + type: str + number: str + + class User(BaseESModel): + name: Annotated[str, Keyword()] = pydantic.Field(default="Unknown") + bio: str + age: int = pydantic.Field(strict=True, ge=0) + address: Address = pydantic.Field( + default=Address( + street="Unknown", location=Location(city="Unknown", country="Unknown") + ) + ) + phones: list[Phone] = pydantic.Field(default=[]) + + u = User( + name="foo", + bio="lorem ipsum", + age=30, + address=Address( + street="123 Main St.", + location=Location(city="San Francisco", country="USA"), + ), + ) + u.phones.append(Phone(type="Home", number="+12345678900")) + assert u.model_dump() == { + "name": "foo", + "bio": "lorem ipsum", + "age": 30, + "address": { + "street": "123 Main St.", + "location": { + "city": "San Francisco", + "country": "USA", + }, + }, + "phones": [ + { + "type": "Home", + "number": "+12345678900", + } + ], + "meta": { + "id": "", + "index": "", + "primary_term": 0, + "score": 0, + "seq_no": 0, + "version": 0, + }, + } + udoc = u.to_doc() + assert udoc.name == "foo" + assert udoc.bio == "lorem ipsum" + assert udoc.age == 30 + assert udoc.to_dict() == { + "name": "foo", + "bio": "lorem ipsum", + "age": 30, + "address": { + "street": "123 Main St.", + "location": { + "city": "San Francisco", + "country": "USA", + }, + }, + "phones": [ + { + "type": "Home", + "number": "+12345678900", + } + ], + } + + u2 = User.from_doc(udoc) + assert u == u2 + + with raises(pydantic.ValidationError): + _ = User() + + with raises(pydantic.ValidationError): + _ = User(name="foo", bio="lorem ipsum") + + with raises(pydantic.ValidationError): + _ = User(name="foo", bio="lorem ipsum", age=-10) + + u3 = User(bio="lorem ipsum", age=30) + assert u3.name == "Unknown" + assert u3.address.location.country == "Unknown" + assert u3.phones == [] + assert u3.to_doc().name == "Unknown" + assert u3.to_doc().address.location.country == "Unknown" + assert u3.to_doc().phones == [] diff --git a/utils/run-unasync-dsl.py b/utils/run-unasync-dsl.py index 2d4e0f089..6fbfed293 100644 --- a/utils/run-unasync-dsl.py +++ b/utils/run-unasync-dsl.py @@ -62,6 +62,7 @@ def main(check=False): "AsyncMapping": "Mapping", "AsyncFacetedSearch": "FacetedSearch", "AsyncUsingType": "UsingType", + "AsyncBaseESModel": "BaseESModel", "async_connections": "connections", "async_scan": "scan", "async_simulate": "simulate", From 3638583ca2371edb6049f754918d36b249e22b1c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 Oct 2025 12:03:34 +0400 Subject: [PATCH 10/36] Update link in dsl_how_to_guides.md (#3122) (#3124) I'd like to temporarily divert this Store and retrieve scripts linke (cherry picked from commit f5e504d50d6064d8d0d87ff2b9b529d2e2e72cc7) Co-authored-by: David Kilfoyle <41695641+kilfoyle@users.noreply.github.com> --- docs/reference/dsl_how_to_guides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/dsl_how_to_guides.md b/docs/reference/dsl_how_to_guides.md index 0d0370a23..816e805e1 100644 --- a/docs/reference/dsl_how_to_guides.md +++ b/docs/reference/dsl_how_to_guides.md @@ -927,7 +927,7 @@ first = Post.get(id=42) first.update(published=True, published_by='me') ``` -In case you wish to use a `painless` script to perform the update you can pass in the script string as `script` or the `id` of a [stored script](docs-content://explore-analyze/scripting/modules-scripting-using.md#script-stored-scripts) via `script_id`. All additional keyword arguments to the `update` method will then be passed in as parameters of the script. The document will not be updated in place. +In case you wish to use a `painless` script to perform the update you can pass in the script string as `script` or the `id` of a [stored script](docs-content://explore-analyze/scripting/modules-scripting-using.md) via `script_id`. All additional keyword arguments to the `update` method will then be passed in as parameters of the script. The document will not be updated in place. ```python # retrieve the document From 85975ed11c1b2d844f76d4cbd383c8282b6b604c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:37:20 +0100 Subject: [PATCH 11/36] Always return dense vectors in searches (#3125) (#3127) * Pass explicit field list in document searches * more fixes * use exclude_vectors=False * remove unneeded source call * add missing type hint (cherry picked from commit cac7ace571e8cdd6d7853565ec4a4224f5dadca5) Co-authored-by: Miguel Grinberg --- elasticsearch/dsl/_async/document.py | 3 ++- elasticsearch/dsl/_sync/document.py | 3 ++- elasticsearch/dsl/search_base.py | 6 +++++- test_elasticsearch/test_dsl/_async/test_document.py | 7 +++---- test_elasticsearch/test_dsl/_sync/test_document.py | 7 +++---- .../test_dsl/test_integration/_async/test_document.py | 8 +++++--- .../test_dsl/test_integration/_sync/test_document.py | 8 +++++--- 7 files changed, 25 insertions(+), 17 deletions(-) diff --git a/elasticsearch/dsl/_async/document.py b/elasticsearch/dsl/_async/document.py index 53b4f12c3..c3631260a 100644 --- a/elasticsearch/dsl/_async/document.py +++ b/elasticsearch/dsl/_async/document.py @@ -126,9 +126,10 @@ def search( Create an :class:`~elasticsearch.dsl.Search` instance that will search over this ``Document``. """ - return AsyncSearch( + s = AsyncSearch[Self]( using=cls._get_using(using), index=cls._default_index(index), doc_type=[cls] ) + return s.source(exclude_vectors=False) @classmethod async def get( diff --git a/elasticsearch/dsl/_sync/document.py b/elasticsearch/dsl/_sync/document.py index 07bda6ec1..c37c7639d 100644 --- a/elasticsearch/dsl/_sync/document.py +++ b/elasticsearch/dsl/_sync/document.py @@ -120,9 +120,10 @@ def search( Create an :class:`~elasticsearch.dsl.Search` instance that will search over this ``Document``. """ - return Search( + s = Search[Self]( using=cls._get_using(using), index=cls._default_index(index), doc_type=[cls] ) + return s.source(exclude_vectors=False) @classmethod def get( diff --git a/elasticsearch/dsl/search_base.py b/elasticsearch/dsl/search_base.py index cd690ddae..c5b12c788 100644 --- a/elasticsearch/dsl/search_base.py +++ b/elasticsearch/dsl/search_base.py @@ -721,14 +721,18 @@ def ensure_strings( def ensure_strings( fields: Union[ + bool, str, "InstrumentedField", List[Union[str, "InstrumentedField"]], Dict[str, List[Union[str, "InstrumentedField"]]], ], - ) -> Union[str, List[str], Dict[str, List[str]]]: + ) -> Union[bool, str, List[str], Dict[str, List[str]]]: if isinstance(fields, dict): return {k: ensure_strings(v) for k, v in fields.items()} + elif isinstance(fields, bool): + # boolean settings should stay the way they are + return fields elif not isinstance(fields, (str, InstrumentedField)): # we assume that if `fields` is not a any of [dict, str, # InstrumentedField] then it is an iterable of strings or diff --git a/test_elasticsearch/test_dsl/_async/test_document.py b/test_elasticsearch/test_dsl/_async/test_document.py index c986e588e..a110d1caf 100644 --- a/test_elasticsearch/test_dsl/_async/test_document.py +++ b/test_elasticsearch/test_dsl/_async/test_document.py @@ -821,10 +821,9 @@ class TypedDocAnnotated(AsyncDocument): s = doc_class.search().sort(doc_class.st, -doc_class.dt, +doc_class.ob.st) s.aggs.bucket("terms_agg", "terms", field=doc_class.k1) - assert s.to_dict() == { - "aggs": {"terms_agg": {"terms": {"field": "k1"}}}, - "sort": ["st", {"dt": {"order": "desc"}}, "ob.st"], - } + d = s.to_dict() + assert d["aggs"] == {"terms_agg": {"terms": {"field": "k1"}}} + assert d["sort"] == ["st", {"dt": {"order": "desc"}}, "ob.st"] @pytest.mark.skipif(sys.version_info < (3, 10), reason="requires Python 3.10") diff --git a/test_elasticsearch/test_dsl/_sync/test_document.py b/test_elasticsearch/test_dsl/_sync/test_document.py index c4e0cc31a..a4f88e501 100644 --- a/test_elasticsearch/test_dsl/_sync/test_document.py +++ b/test_elasticsearch/test_dsl/_sync/test_document.py @@ -821,10 +821,9 @@ class TypedDocAnnotated(Document): s = doc_class.search().sort(doc_class.st, -doc_class.dt, +doc_class.ob.st) s.aggs.bucket("terms_agg", "terms", field=doc_class.k1) - assert s.to_dict() == { - "aggs": {"terms_agg": {"terms": {"field": "k1"}}}, - "sort": ["st", {"dt": {"order": "desc"}}, "ob.st"], - } + d = s.to_dict() + assert d["aggs"] == {"terms_agg": {"terms": {"field": "k1"}}} + assert d["sort"] == ["st", {"dt": {"order": "desc"}}, "ob.st"] @pytest.mark.skipif(sys.version_info < (3, 10), reason="requires Python 3.10") diff --git a/test_elasticsearch/test_dsl/test_integration/_async/test_document.py b/test_elasticsearch/test_dsl/test_integration/_async/test_document.py index 274db1027..0dd0de0ca 100644 --- a/test_elasticsearch/test_dsl/test_integration/_async/test_document.py +++ b/test_elasticsearch/test_dsl/test_integration/_async/test_document.py @@ -864,7 +864,7 @@ async def test_dense_vector( class Doc(AsyncDocument): float_vector: List[float] = mapped_field(DenseVector()) byte_vector: List[int] = mapped_field(DenseVector(element_type="byte")) - bit_vector: str = mapped_field(DenseVector(element_type="bit")) + bit_vector: List[int] = mapped_field(DenseVector(element_type="bit")) class Index: name = "vectors" @@ -873,13 +873,15 @@ class Index: await Doc.init() doc = Doc( - float_vector=[1.0, 1.2, 2.3], byte_vector=[12, 23, 34, 45], bit_vector="12abf0" + float_vector=[1.0, 1.2, 2.3], + byte_vector=[12, 23, 34, 45], + bit_vector=[18, -43, -112], ) await doc.save(refresh=True) docs = await Doc.search().execute() assert len(docs) == 1 - assert docs[0].float_vector == doc.float_vector + assert [round(v, 1) for v in docs[0].float_vector] == doc.float_vector assert docs[0].byte_vector == doc.byte_vector assert docs[0].bit_vector == doc.bit_vector diff --git a/test_elasticsearch/test_dsl/test_integration/_sync/test_document.py b/test_elasticsearch/test_dsl/test_integration/_sync/test_document.py index 62857cd9a..ce6ac03ad 100644 --- a/test_elasticsearch/test_dsl/test_integration/_sync/test_document.py +++ b/test_elasticsearch/test_dsl/test_integration/_sync/test_document.py @@ -852,7 +852,7 @@ def test_dense_vector(client: Elasticsearch, es_version: Tuple[int, ...]) -> Non class Doc(Document): float_vector: List[float] = mapped_field(DenseVector()) byte_vector: List[int] = mapped_field(DenseVector(element_type="byte")) - bit_vector: str = mapped_field(DenseVector(element_type="bit")) + bit_vector: List[int] = mapped_field(DenseVector(element_type="bit")) class Index: name = "vectors" @@ -861,13 +861,15 @@ class Index: Doc.init() doc = Doc( - float_vector=[1.0, 1.2, 2.3], byte_vector=[12, 23, 34, 45], bit_vector="12abf0" + float_vector=[1.0, 1.2, 2.3], + byte_vector=[12, 23, 34, 45], + bit_vector=[18, -43, -112], ) doc.save(refresh=True) docs = Doc.search().execute() assert len(docs) == 1 - assert docs[0].float_vector == doc.float_vector + assert [round(v, 1) for v in docs[0].float_vector] == doc.float_vector assert docs[0].byte_vector == doc.byte_vector assert docs[0].bit_vector == doc.bit_vector From 1a36996547cf4731053ed93c8cd529136b922cc1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 19:14:44 +0100 Subject: [PATCH 12/36] Use a nicer screenshot for quotes example (#3128) (#3130) (cherry picked from commit ceb2fc4bbaea6afbed0b1c2dd96f1c947cd973e1) Co-authored-by: Miguel Grinberg --- examples/quotes/screenshot.png | Bin 229765 -> 142811 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/quotes/screenshot.png b/examples/quotes/screenshot.png index 7b546085cb7243cfc7438ceb2e79c61261ee323e..725c9a0c8c07af2e92dbedb9dab9dc2e5c0ea7a5 100644 GIT binary patch literal 142811 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
>&CZet6MWyBmVKKI8;CnDkw*ZR8& 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 JQpJZs{|A9ca%TVl literal 229765 zcmdqIhd-Nd-#<(#YAZ^uShYvBY7<-SJxVLKDr&0{LMcj#)zT_K?Y*^DT1phHy-RBE z6)PcY4>iq*h)6w% zh_202kP+^X(t=lsh$yQ(G&D?fH8i+Q{C(U!yj+Qh9=?8?MQ&mKgAK8K%Ge z6227em%qXfIwali!NKP_c<_Dq^*l);lgvlg)I`LnRM~*~tjjA#Nr@>1O9|nP{4mnMkj~jRUhfto zN)4N+Z5PqM`!hmFlwy?B^pnU3LT7w7 zcpdrN%x0Ct{h0Tip1=N$QyBCp75%D^{)g>nFyI^$+Iii-g*EpQbbjvCL1FilGUtHjiN;P*+Z8nqZz!0r1^*_@tbbL7k9@&HYds4Bur71 zBcyF#DI)IMkSP8k{T;<}ND|FWtr^1{MU$?|M*q^%L41akQk7wbHaJ?(mxzP(^bds` z+1xi_U)sl{*587B*_DW#?o#H(+&H8)P@|8H7oul2i*DkM=x1t(+f1cey?$Ml$3&Wm z{Ej;KA!+q184WEHVT&7<8Z;xrBf6U$fw$SB_SJeu3 zC9FA+##VHDOEWAjCxJCI8vOmwT?=X&=QzJ^v{Sc~mjRiZatW*X>vBYsW7u6;sh;c` zL)K!~D^niyDmcBL5%S^`^B284^te}RCEd@{xA$|z z=Ub0io)gKw%=x}@BoboYabJagxZ8b+>M;B;{>c4E_V5#$j#T*Nt#2Rx7$)!>-UyBL zSLa)zULtdRb5|?#&Nd_G>u60{zX$cPAqg|O9oigsjM7#g#__wGu?oL$dTpWhOyCK( zGJj`Udiq$Je=6m>WnJ&gic|%WAl+v++kB7mywuOQ5$Rg1-m41!EEp~f%`0`O{LiLN zLXR@NGsy-*2lxiaJt~^MNSlA)>r`vgYcsrvJJG*g_@;|m!|jzpjlq{8zxST^Bwb!N zkTq~Mh|QihST+c{9`qXxn66c=URxVmOdb7ta=klx zh)lA#D&iwHUv0iVsS8p&e#uU?C|W7pE*$Wd>aAU-k0`{fpgg3!sBG87!}_*aZ`p@3 zvdS8>h1xK+(^nr}e#l_6t2Wuoo{I>wS}i+FaWf+8D!Z!}L$4wjXRQObc$0pJtrA*g4;Z%ifhvke+cGd44!yGMnm= z?K$Iu9!YRoeU|BH;T$-~>TV!wB(>-cULcjJf1rD>{N5Kg%AC3PD1-C&v|`^S0&V(1 zHjRfVw`botzVn`Pk@2c=;d01xUwn=mQy=E-bBfoh?X}jo8?+eIct=;YT8*tjRVh^4 z+vV6aR##T}%$j(0c+1QL&UDziv?a7Z#TT}>Sj^vPvGZ#8hR@8--`M-G*SB}NyRv&e zhb#~siP`Ku(WkW`e-TeZTgsuLFemRO&lP|j|1l)oTRytA<K7l!(qki_d;pT{62W#uo2IbEn?^eBW27|E>_V#>((@y*(Ryd{gB-tu?UF*g7KhC^P z&zuFaLdEBsy`-mPTDXIKzbv6Xu4P%5)jWLS7gju^H#M;0H}kpwbKmFTDpe~Q&wF{d z;j)o0_r5LujQr)dlIhp?<=1bseVdm7>K@(Wnj7D9U$5xa=&tEXhH!Q0Empc9S4n;x zm|rvBHtQ%Z7+W4QLakwhv3Y?%Pd=UyN0;ZTa{uxjnt3b5bHXy^rv85LzG4H@ z$hC4~7yb()>5qHqPu^ZRT7Iusk@uI!e(ahQsBEp_8a(}VpWQx5m9t_{Bl|I;8@Bf*LVZ*Q=>vSDsYRXmHRBe3S zlg8hXAf3B5B9QP&x1GFd5S9EZ>qN1nfg}}{#-v)T!>L9u9kS()*N$Yp=q#MGH5VS? z8^4osVfJP0_gGr4esIg%(E-ZA#eLf^JN}qL6iK^oyHiiR0@7B#lOVA(r4+E%sX&B$?IGNZ%w!& z1_f(JYMVgK?Q>J^^V1iVd$rY9!B^hXyR|`&UwmGauM7DzL^I94@jPs?BP1zwXM3-> zm7J`H{9y5QCtug#m&+6T?N00S#sg#J88rQ6o$tfoGHlw#rw_x&`#Lm z<4XK=JaVing0U{P>b)6}8vDmoqOUs&P0tg^qQW?d#u{eZ!us-O@z?H?;uwdg7Vo=} zt5&lUjo-1hVIjI4ErBykYR?DCGtFIhU}x9Osf>2tK7Bt&S}l^cWM~y~{;dFU=MO-} zZV-Rx0CJ{Uc+rXjB?a7&G``;dm;hg`Ty1pS3=N4y2xST);&=}tQbLKC@L?l-h=@qi zJqNz&i<~h&jX(N1p2p&mJqs{_OP%C zv@tYLaQ5+*aCGr;a+L`2_Wj$0NGU{tQ1o^UbmR{4_Ie(m5TeZcuNn%3^55H%yxjk) z66mSSYh!4_t>NSE$}J;tSK=e*s-jlp{Pn=LgJRtOWpks*m^8mj8>g2!s(R2-P_V@4&^zeDk{kLC7 zC!e4|WnSLD1O4aczvk&0;_*KtJrDTzYY|>h^6wK#DT%w1|LL31RO#YQ* z_n!~{-B3yL@2meq6#pgXf88YrT7^6uN~Ei)W)VWXn@j%DUTYFB z*uIq<8*86#QX~)+t4XSUP@tP8U>S<1prq^<|RYF^(G;wDw25SJE?9=XO- zKuYnTD0OiV58l{62z7%+Afr>ZV566`z(W>!6S~_-b%k`bhKW?vPDZEAur_#$5dF0t5 zfN)qe&GID-0Exu?0I( z>U75k*HIRr?pU#tT*D*c9FM*-j?)PHX?Q$AsfcRBrM~dsC2ca7NxNho@NR2t_#~mL zt;6ZM*EF$18fA1^o;+8X{JvMF$?owa8E2@bimUPE1bJ{`d1f#d^630ZQqc;F9)x`w z3n1UPp}D~d#IO3Hj(tuedTN2Uxd_8!bi!y)F&UI&w#WT} z_~Duln9#Qac~j9*0*`~TtEDGXt>(VP(g=r1bbBRvY1C;T=VYE#iRbWnj%>N)8lBXu z9S&;eIyr)7eyu3ty6~8zjST=0fARNaRq%DX>92?3VBFTR4;+7ZS^F*m|KrQY6;__L zj$b9=PJ=bSud#|&)-kFkBrx$9YANdL6_%+qTV9@MvngT(p~=AtC=pR(F;sa7cGn>Y zdwiL41`P!940kPGg7W%_1%Wu!2Gbb>5MO8Ql1)Kp#tAGcM`Nak;kzV}S3h8if#4!U zVhR9`L!ZVwzoI$XsRcoT^0)~?P!d}PYlNgW_~r5#_HF}9YGdUrtKUQdDC}tk^Q{69~ z5dLSkia72J1iKe~U0N!On_juOeM`|?nU0HwebAU`Fjh?fDm%mNz{evwlxbG_zXn9d z7;yR2seRQ}x6c+XHVU0hfA)zhjV5$bS7-K!{JqkmLrhe1Y{y-5yLvI41Huc$$}@l^;IfjkESaaioi2B?NA@`|9Db;co1FfI*dTzo9BZu-IQ zhGZZ`*d%kwL}?@#w{|QEgJ7_O8=yf5uJ?-#oMgiz%k2#j@K$%P1+b$#U^>7~~2&i2GJ@rA;WxJKcL6NWCRDE|vMH>LUWyw^(R?RVN-wKX==4JT=pj zua@M4MbKQ@I9zqiioA0!jdzCU8LTf8Kg8R}P}5+Y2-qG&XEN*T9o~T#HJRULM{SFS zPCk;kNC)&Ci8)k&V3id3JkgX&UJT8THf94G4qdF0h~O48zB+Y&O1~V0G*f@O7q4 z4?*P>orye!$w9c6DAmAz6-!H#JM z6?uUj)s9~ADBkp8v!=#Yc+Cn`5N56!S&o1XOo*nq23(ndx9X131WSwgc{F?mIAi>N z4)=ohkMwT@b^Iq{=xmVuomI~U2qRC5xE_MWHYy0H;Lk4U;P?TgPw_eE<9+_#ePG?> zY0Gbntd0KD%8=#-eCdW?vo}LIMcl2?M?3tgd>2cnEBWTRfis&3O&#SGtt_0w>bT4* z!>sQo=)>XbdY=o--*l_IWdupB#y4QobjEBll_P@;VzX9;7fmBTw`6ufyT-3B;Rlf0 z5|P-o;iNftEVgIV;_9)+>@a*%Y81D;0Xm}v;{7FDk1@uP5*yPX7z9nQO_&P)*YN$I z1GNf_>@+p1oM{n%4=MD~)1ek4vNyZkh8ro-N+6rMsZa?>(rt(&3cN67O%iysSwBvj z@8=}SsWJx0X_@#T>xxYsGjjRatqEdmjVzJ@GSwgFw z=wV>?`F8>;U28BV7#ke%0vmxJ1OOtj7z|<iaK7YHT-Yf+pB>x6vA4U4Z-X26T`V_9pI`LcR++e`OEs^nlOwHiHM^0EPf8B`0{e-ax?Vs!zefY~5OigRK#KB6 zuiQUDqZ-h%JPQ`1HIGl68o>?U=z?zc8u_J*3qzz*=}wbc61BpEO)y1<8q4Ec_=jvT zW*ROaY8{7E@N!>}v(&M+I%z3=s>D@;uJgqPynwycr!$}33+rLYsCo^=$6qY1&YyEuG9-3!O`gx=|}@@NV5`W8~^A|LVAn z2iR-us4~~9OegCd72@D;sA53kQL7axe4M~YEHe-v4TV8ec^LO(w=h$~1SF(;P7cRe z{SE6q8vqsj;a^Z>tCx*3~|aH+$edeKD1v24ai$JT%_h6j22D7g=DdTF_UL zDieUUBMpwN$;dCIL<=0GzGDEQKO;OBX_k_951ET*paOq9DrxM%hY4nS{l5MqH^nd_ zZ)&r5SqDT_T%C#(4ZV+EYuPo6-hwH}@N+^mmfh`9it;LBM;X(R`6|pQuzNG1*b6+4 z%r28j0|A7np|1W)81J${K|pl1FCwo7&u+kR*0A=_UvLiRSJQPTSi4Hn*lBLYUnZcQVx9Pf6Xg?SU;60YM1$J_?s zHrJNQ^52(|Ud3f8>z=pl8h?!cQhNuwnP(}p>+bb2V|xx?W;`!yJa2Xk=(VQqZ5Gfv zfQ&l#Xxq(>=P9X+tyyZf{?_7_tWBYCS&~)@z!~9*4M3F19ThwZQ#uT9 z42VHOq-!rF*Cnw6H-AEOXWbc4ZSMS_Usy?RC}dq{lM({isuH1G>&D13kC_^S9**No zhp`-^^(7b5U2|!L*6)J{A5Sc~a(x!bCo>jNCAt|sL?1PwOZSvQf&aRx?8HU=VF5tD zWeaMK?kRE=F!umsO(bpsID@|P)#h3rO~&DVl!8a6omOg%!E-CdE0S+uPHRUrLW~3@ zSx4ZHSB432oe<1%KS7lif#nZJc6rZwig6qM6J^0lG=N>HcH` zOHkw^ohfJ`yXG<#2WoDgvcs~3%8FwxwYHb%M&{3uAGD{|L=r4%<| zd?A2r?lpd|lMGCjgm{R+*&y}k#MjBF-oAsQV|e?@uxToAwjNn@nVWK-0AC{dpzz(_ zI~%ZFxuqFhC<~`xBu+enz>J_ENZ^9y_AzW%X?YsCyJ}Fd$n<6?xy)U+yi_eF?y<_a7qmE8nmG3>d>se-K5G; z)@yb}-+ao#enpQm9w?E+)=>bNcso%Zil;GvN*nO3Q#`?Ex@^E+482)~^-UJoA&p4H zqYvoqB*gHcfuAon=>Xq_ccqZ-J32ssey-Hi9M>K|y6{_{c}{mgKQVNlH*LvOKH!;t z0qe$(tgAX4g`cV4DXFHF8ijr>pjFOM z<%60a;zGigAHk1QrbVnGC>qkrVD@R1Tzz#8z zz1DWv5jvS<_}#97hWq0mA*hE-=rvedAohgOj-T`27$U`#(?=oL@@z#?(cO1{1e2_^ zyaC&uCh$79%j@{dQ$fY&U%&n3cZV43;OB=JcO0(gFOhuy{MctbKCK> zOXRN5a1DO-y$;JFewn263@>fJgqxsfx;c+8xarWbe z44)H2i8dSQP;M$!F`ATbdL!jxz7{$I&;mfZMS9COSs>BNWUP>uwMZ9u0B~grdsy!7 ztdkCHTAcX8nW+>sGf)dI6PHN<_t&(bo2o@sP7{m$L}{gzBID??xlmzZef3e61E&#^^1Cy(ZonyLO$q3wAnCA^f7-U^)>0?1a!Z2Kw4?;&F z(Jip>0(&uztp#FJux@R)Djx6RG8BGgyt)L2X>OP_ui@-lws6s$8@INb;X+;De1!H( zE}mY&av3j%SraHNEYWAyRnP;GWobb+oNs=&f1D+nYQCgn_iaVBDr5iVb)j;ZL2XbG zMWlre``hO{(I|P$yY!)$&m-2 z9Vc?@3KE7LT>P+_0zqxb9r6HC$&ec@H!(d%5rTJAROpunX`ejP=_HNF&{s$;p?-(s zrhe0)%1uWvoO*3=LOzNzh+ZCDn2Q}`36t`iI3_7e$u!(MSurtEv*wz&82Q8(xgD?6 zMc=EV10P4P&W@q2-UKJ=zVC1{efMyBifOISy;T2lnSeI_(xR8n#7lCgE0XFA_~T;& z+6b!taljmEfW27x3(;3M7#s>bZy=I!sKKD#4Uh`{1oHxCPl%eR4Vv8hw#(RzAj{OrAPkfTHaZLlgFEexArpMmw6M5f}WLT zKZ7dgc-4ObqVoYax9c+i0|U+(7ou-qE#^5BT?I$4iV1jr{OSimeF<2pJp>5%cx?lQ z?JDq$hCz-Il$SbRrh#yr%jHe{B}NtqJKiyF0o56E|6RSIHSK!i$FD#T?8T>mUP4mI z&Ann*u6pO9_Nw{tVwiQ}79_J?TnTTgEsZZs)?5)ZMCxipY{xe&f0{mum7LRapUuvy zb}F>CztRm_&C%R^Y_3nK6e{z2Tj;(oF<+-@ff)C=MR3!k&1wu4MCy;nr{I|q$kEsM zZE^O?9%zCQE~yt`ivYkea-6}Nfe(z#n_?ZX0d2ntr&3xXW@!*0C)*X zqh6t4*2@sj9lm{d2QrW&vR$V$&98n~LQj+Ui=>Ijft&3!S(U=<4WfmI=Em$YX*}27 zE5yodJ~TH4Z(BeCD7|rXLmF_Pxwm!uqeE7oAV{n$Sg995>_>^Ho$&y3Qco1CSIsS( z>#*-CMV=y&+@QNGycO->GhW>1IToY{*}?mRctb^Gv2(|m;*yQh=bZV} zVEfihYNem`>a#dQF&bHYI<*+;pmGyI{3^TU*f*IHXBt}AH6wz;gw3rY6e`MWNNUpM88+>)a2%MY0;$8TE1=S4onP85uz z#n~gu@Ds%=r3UPMAUklgY~#~|qx&2jgt&r5|0|d@?qvLRD8kC{S6laUSUkF_Fpb0T zE1a_{9KZZK|I(;<>q-zoT^Me#&g9Ui#9m;)sUH3luKNHV^|`=6x$rFWN8^vFhA)?( zK(B;;z`!$Tp8u1ha%+f(M)*FG8Sq488DO?xw&6F&)Xi;X(*Ma>gH^1MI73GVa`MS3 zXKFCMJV2&n>9%6DF_&U269{c#XSPS$wCBPu>^I_BoU&Zl(>iw)QyRfYB@wF=q;Z8h7XeYpA={L8y*c>=PB!sy zzcA-S$5A!GOdy(C6L`%vHtd4;uCTL4h0KI087sKxY=(F9^pi=S3UlPdnfH9lUg(cI zS=8I}tq1wd)AsHwdCK86<847Xw0m>ScEy&JR?*%^%e@7Kg}h?6O+pq{R1Zz#K- z=$m9)SU9xvPr2^h(weW5XzU?79 zKwuD;nBif38`8W#!0TVq2d*%WOA3#K4hw^cCo8AF}Z)_x{g)=!U^ZxueF~ zt`Bm--xFA1_{rMH)9+Z!ap`PRK#4c6Tjo80Ak3fgcai5Ot~}M>(G=Dvvpb^0#q_%#Yw zT=C@K|F(5wLPu&tRPc&^AONBguzup#ij16}+<-xFG3(FMy?^iQM1sKAR;hSXh$3*> zKvYGsSOfQ2Fd9)dS^4Xo@HMBpw z&Q}H@0I|eG!Whb~t@wZKIjIh65QRmRim8We6|+27IT_K2AIg#Sc_|!9F3PCEAhgW< zP^&0`>ML0dQRi+M->|@QwcwIOEQ!oAV3mrn-ZJS&mg^rkRKK8%9-UD~XnwfMtw}Pb zIw}l$YWyguX+>EIO)O^?$rL#A)I7Jma5J9U_07UT`vDGiegOO<$0d)_jU-FQBjqUS zc$2OY-`cD;GTSEmgJq1!7n1l!3K^a<;~Tm`)*%K>4Gm+~N-dknam9a1UR9pL#X?<6 z%av8`a2|ntfsCj=oA-h1SFO3sug}I2=b%aCh|$D>%YZ*5^t0(KUwCQN_ACWcV#8v? zuzxggmhKggBPukI4yNR6yZIG7oVTQ2Ceq#`A}cZ^BDl@KDns-`MQ}l>G9k)Os(8n9 z7SSm5slPY;x>DJ*mIdm9SLe&8SkfAeu<}YI#}Ta#y}Z8rlBcT07CO=up@>AV(L8yv zO5sA`yPv1{!aiHtLyuo`zR81;aGVms;7iIuOi<3hE)Kogs!3vG)aWU{tjG6Oo2ew^ z4vg6A<%o_b+VI90q9gKUa-l&k&TezXWq?qRTEZZ=d@msBK`YhrfR>EOl35nzZHZfP?g07^eX5f=VYY(V=5O6iMCvx}T zgOtT%HfG}I9P%EiHSPPh1sd^ZMkCT9-TOlf{q#|fH_*#+`cTFH*MxPVFaW@g{Bi=H z(FUKYjFa3;r*nK1F$*`X8kS^D9Y?&#y#}aa^`5vz`|@lVaF~=3Z-UKi&$ka=W#^|H z$?vmF-XtaY%2GbCEfyw2meZ$CUVHXTpHuy|X(zU*(rrT9CE4lEr>pBMk3Z|2?=Q%% znliEI2Bo^dmaQQ zc~-}8e33zzqA&5>ACj5x!l~1w=}gKk%WOhPcBvAluZ;FAVDBdLWc2Uo??jzBl}d9f zXmS;d+g_=2edFS}E)>h8IQhehWHQ`CsHepto(A{^D^ybM4>Y*+ZoOJp6*%ikC$o9X z#z?%V>pD9zA6_AKl zE9oHXc!?(6{zLVL@}bu5L@&@mFfuyW3tgOxHLaCvC^on<(^bfhlPhH0rq9)WSZv;@ zP#Y~JN0X{jR;?=PMJ^RX6V@rsYBzvZn;sPQ;S0U=~6`@S=zHF_QFI z4zoftjjti9uamvXdsujBHM&Mjp?hYpAEp)isS9qgayk(e&6(jhNwdlL6;HgV6BLMf zZs`S0G!|>V0WT+o2#;{K1#UH!;Cm-c{5%3lLwJK1=^33^>x!M;CsjuK&g54=5(_8gk8iQ;St*CR8y`zo@x!__bkkVaZCWO zSJ?Q^6x#_%ZqxiY>5@iLH|`>l4EG+{AP@3M4|ARHfu#Y zIjO?D-UaT?e5tGrB;sQEZ{Pg4i~jqP@5=i%A|gxTNt^A#68-X3N^+;=`9euj5a_q+ z(iJ>}{4+8C8la5vTy=n~m0N|_E->}pYHv^xmPEmi1T}DCsg${^S2`!eMx@fllY=Eq5$+8sv_)1bj!3=6=FDIMN%9w1D?A}or5f_ z7eU*3@z$Sd;_aQlK#$n`z=yf?^mqBT%P4Ls7~98~DiTDXmfvIVI@rfLQ>VL{!IVbV z#~dMMo7t8igF{lr5`jW*KX?|Heb-UF`jf45@&k2bSI#`@bMeBOC(X*+|Lb`U!&ueH zN`ueS?khOJ%{yy10trXCi10)_Uj@QHlQgqHRo{2)W|sVpu|$Lg&hQm|Poo74ti&jJ zbZU~6S@Yc&kRuUb-g26?F)~L5QrcYI0G5Zo7gq?6^kqy2xK;!mpRmx8X82YBJ0+X+Gi z26;4QQ4IvS3^t^KHB6+M8 z^XaiH61#w%)mQ?7RBrbf;Ds*cn>-H=b055*Lew@wp4u5h){+ZDAD!-ZMxxhcTU)#< zZ53twS9ct@C(4`WLjIzlstyY9)#=1YXL##d79zlxe*zpH0St3s^vTVJ=eKC zC_(v?G7UP`{jw(74)im1S_o}wF!#g~NQpF?BrFWMZhgJ3Pj_-&lsn-q`?gs6)BdFnNl?Vqr@g1;^>b>Kiq=6__0P2&?Sb zXSpU{u6)#=A{)gZprIU&l+cxzhgPEDL#6ue$B4^J!}Vc0$G)Muwe$zwdv;H5PCc`B z!#7OfAeC`)?Wo>tqzHf0l# ztbO!0pe^&ge875a7EPm?;z3hp9$PKO9Tve=GAq@AS2o@W~0gxz-%2a=Cll<58M>^WtR7GUxrY{Qo-rB&I_eVpiflyiZb1wc;HE zh+>bTQq2_9Dl@*pwmZVpP%Oq-eln9D|H5IC2765bePg!LphD@RCJ$aMch06In8zVP z^Vc}7kWEF8J7t{H$RnpQ?Y%O-+Ha{1qg-~U47qa&Xf;8eE>(WxODk-Gr6f!GlU!lZ zry_6iw`3x0pLX_b-?DV9_GoV@$lVeVG`=w&d6iU$egUcDvSQ%Bqty7)t)4-;(gJ*2 zp=wSUNV%K*c&o#9-b8+F@a|79kr%j(yRsL&I!?hxMs|RL+d!YGXo)a&?;)i>} z99MJK9cR8)YRp@m+7*~PvHsM%nzH-U1IJ(aqPHtwpKedHc~;$`Rq}0@jcs!X)C1UYvCZyyxz}3xvr22~j`6@|9_S;HW}+O) zHa(j7dE63@nJ$L>q+Ifdd9%(r20}oOffJw1!KlqK8^ZAft13Iqs0r`)k4AW|@ohIL z6w~HINnaPEjlbReQrJ?&R^>`KOtZ{7PruX8s1ajjcSTe*#$epCY9||8R337&^!a58 znVK{0CsyIHHbh0iacph(8%JpDol8L3hvh8;xHAaF8JcN$!FHF-JR!)K3J>yy7R^M zX-_$&v%rH+Ix+L~l4rX1=)+GhV)6=H;E!!X&t~y`mU6rv&pWo_ii5uVo*&MUl^n{J z>X|tj6CEI zR)1cH%heWB_grrMgXVtq*ZRY3Cy&P-OTIEUR+h)rx7fI^|H@@2u!h<;$@Y+e&S^*4m`4zhNf(siVN@TCRpZM2y#t5h^TTIr(buQ01 zn~b)^sSR2iNVGdgF4OQo@Pa+v6D{iTXyj7m;y4;@ehjKi=jt>^eqLE6^a4s&?2;tyb0 z;OHb~;|VM7c%(-PkGllKYQO_DPacHeV?0l_NM;#VKCc*2f{ZZ=7s8W*39s<-Yw?a* zx#tO@u6Mgo1C>2fcBgbXJ}s7LIMUwK`}#aVk3PFpD>2zZcO>CT_dZ6P;onuHgyH;0 zN(^`EF19P<8-FR>H~1he48|Uz595K4IV16xmHyt4^LEjm=9abuMocdYi0x?J1M#Fs z^p?iGpzWpvK(@~`ets#Moa>F{n}eqDUkPyX>=7>EI7RqRg5TcWdBuk_`|a$C0(piT z#9ujlN|vF3}X_*ahAQYLge*_h_kv-6tjZ z4~}3dn(ecV?i+K*mrAI^I(`)DN1k8b))ERydxMY{ag6$ixnhPSkkYgP*dMJe|HIg# z_)KK4A@R5yqnq26wRA(bsjhPd{k9)$87W!I;;S}6;gEfTdBhqm>bU5zIhGZ`cQs!f z8W=b3TZ2?>*bzhKEFnG&a~F5Uf_Iykf{Hyx+Po}lqde{^WCW+QO{5U>Uei0UUz zts9d}d1)L|T-7p=vCN{ZlA!abUX0!Led?6{E$Ah;%9MpWU;CpsEkUdjc^A*uYgyGk zwM;D0%`2?_EI-Jp7MH%+-Md?UKgV>YO`3`nhemkzOj-HV7{-TTY^+&Ic z`#GL3yr4;??oZX1q(jW|85}?kjb?n15#6G-Z{9$qbAvC+mo_$X*>M-GK%Ms5z25%D zN~0+qPE2PV!G>Nz5pV#l%BpHR1I|MiWC1>yVeEE{2e2)2U4{KIt%6U@Y)XJ7n!beb<>U zlW9&TJbV*2!(I)q}CSIb%PKBh4b1 z``%jHPh~D`ujRaz4jU>D+R4-(`pxm;cl}cNl5u>_^S0iE*d^j?%aV{z6)(4g;V=7h zKyssw_S;)Tlm+HO9Xal+m9<*x zuM`rlyY#1m;-4?<_Ab0urB#coI`@CIc_MUuZ+<^~4yoxQ$NGqrc9US7ebKZ`pmMh0wJ!zo`s7 zndaxazBTqx*l>hx@+=*+0Jy7|F1}i6^LW;FzEiftR%ai~zZk58SATVcv&s3Y^7oZR zrMC6-ml@?Myz;(;+)uaW)9HH0uJ6@x6EFMxx6aZBPaMaO&+4rE9mmi4Q2R9{TuLDT zKEoH~CC4xS$qWXmh#4MCKMv@uc7e-$j0|h3;CdN%&RIiBnw-DU_x`n_FqnuPOiW7D zL6VN5s94c9I9(03NR&AwM@5S#J#2t~x9y7!+bz?0nu7vNR2m8U)#=Yast+!gx2pT<4I*2P1(Hc5Yl>kIDc%E zWyM_GO)wEANo^EHnXiotdTI!W8ZbpveLFUiW2p5 zZ%(H7M5;a3EXnLRUErW;o;)8jr{P#k2 zUAtpnFA-$X$P7ZZ)HZv+x0F@EDfk&Xzxn)Gw$i#Kn}%|RmTce(n1|re}k??Z@G8#AyrUSbv1{Y@=2cFTf6Yd;TeX+w11@D6DB?@~= zBN7$orq+C~S4e_&4E1lbK{x8uS4)A)d>o;x3`f^{c52$6@8X!xX2wOd?LR_r__^dg z=yD>Xh(ho#tBNQpGV6321nfG{6txO5ra5@8mWbJQU8Rr=eq|8NG3yXRvuxeSeMj$9 zSV13mIdw+TmMbnriCR_5pY8W?C{4VKqq$POq zk@5RUC}f^S)>X~!|Z=GTh9E4{lDI?2VNfGRtP%P>+Y*bIN^l2t|L=c*1KtxY_f$jn3c7i@Le&R z>CW~H=|R-_kGMKUig0&ai$zGb7sUg)u|El_pi&2HRkVWI%om z%EYixscS9;%53*Ka|{=Mwn%tPhWln?j(P}20`3(KHdd*m_#kgy693+D&%~o8=*Q8{ zK42g1WfL;5()iQ*Bk$vFUOq%=r?ca+R^@fCC+{S$*=jYM*>?JKE_F(RU$PhEU1eHI zgztR;@f!rsI9`7AhN*O}=s%tQ6L2UXyO_B!vF{FTLvu{F%=7;r2O`6P0(Wj@YQ@6*0_aMmyjKx7d&K3kphtgUG_CY*0sP2i(V zg5Ew|xMxS>|0g@+DyS=~TWqU< z>)Cug`jW{Mom0p%vX+j)HS~&c%E67fpw=f97=1*R71MGJ;rw9u9G9X!dWh^t6M+-V zh@`Z4yXCk->z^pBF8iW%OJkE|hE(Q%4W_jR@7*M9#Tym<~QF*y*Bp!Kk}Co0OEuXjlTx$8BOq0;*DC9 z9J%ZaG6LUzpJ#MSCb)C2(Y{YsI#rs=Y>9@b*6CiiYrX?b?Uv8C<`~rO458lY4K=A% zSRNa0&{P||C&TyA^X@bKL|Wds6rLGfQu<&oYJ>9a%)+dE4U4SxdJW?Wl_QR*Nbc>K zsT?`Kf%`$qB@V$4Tv4kd+hbHKrJq-#dDhV3_i(g32?|15_V_wy%bN#Pk{3uQ!s`rk___y*?)AolRuqQY&JHP^)KIw2B zd5U+~5qV0LHZI7mOzx_z@o-3_%R2%peWaFsI7BU5r@{=i36*1hQ_DajMj=F)fSk%n z2%n@M+zZQvp5#&1Xp4)Xe?wQBi&3klNJc7%n@xy(Ydc?p)o-lr`o(yAWOD?HU4-RL zSMPNnS>y3YTWi#Re-ug+PD+$^-0p9wo=t*4ZNT8x!S(ZbKalMzd9WvMTqx?9LGyr= z!He8#DRA!U(t(GGl3 z=iNq;cBQJiCAwJdw|S~ZyNKaovfz78CL=Ty=V7n#zRHjvsO6`t?HRrcd$C^e4%QI>MvgSyk4w?{gmGlBp6+m#157ic}7U|yj(V^j{mKHPTdD-HUuY>wE{cAw< zA=< z`p+Huf4?!41olU7JqhT-WHiBhz#u5V3W(~P!rW|c&`eNol3&l#+V)8)WnYp<_5Sj* zOuxrg6p6~(++saR$8*knahF7;QCtwH9$}~~gBnz{rBYLzt&X4PN}3!xkwR5%mNb%fxSTQhaf=$ zhd{6CmqR>Ws4PbIVvh&tKTpnvLv!@rKKITPN(9r{CC*N~?X*{nXX96 z6>P}GuqzCgreq~O!IgTAMo-ngnM7qJYkPcA3`ukS^pCUsU!oxp4*}K#<^l`o$MH;_ zt0`uQheWy*K)TX+#Y-3Sd*hsj?Q%39o$j!X`~4?63JkTB*9Ap@t#q;5Na*OnjfA2}M!O>d?>u;lq$&Mtz zrhqOPdhu#Xb%%ts)Nvn+PWp`|z+BH_(em5m7MtqZSbEB((_J5p#P%GA-=qN(sEXkc z(o3jiCvDgud8DG>VtG|wzldUY<)HHz1F6ZN}zR=|)E3^Lfb2qB19642$r!SMF?A=yoWbaJ$~qBgh0 zE`CfSdXK0A>-mZZyXv`J-tZMs=l3Bpu2{g<=-N9CbfSOsTWU6`Ct6%}3-TWP5kHXW z=NvwF5<>$pSpqTWKSTqaQ!2}mG!|J9G=No~ib>gLxG(2J=X_K@&_JiO)C-E`l^qVl z#s(2lf0xV(q!Xbt5!(}=8!9e!%95ge8ODDS5vuT3V*WG3Z|XjSAHy&e^QHc_danW{ zr2@t~)ipH27V7pmTO8c?0gLJft$S=WmES)af1>`y`wxS!NI+)NXR^-OeiF(Ih`?iB z1vSuTO?AV3Ym_nmwjDE|9jvjC{K z4o_bN8Y9<4g>A)djs5eq8SaP~UKrcoY&~^E5UxOzt>v2GkLguuyHR+#F3VK4Zwevd z!1;cZi3l_Rf0W279Yr2=QGne~(5TO`c!B1}Nini(djJEjSGJ&|E~uBX%)U=$7F>Y9 z;%0lyf6vu?bYU81rH6>8e*vP#*kBm{AA9DXCiD7@uJ_*>?f%qOnB&B(KkWyV0N4@w zAi*HfAjx1>bwMd&BEjDo7%h5i%N1S~$^M1_A{4|mfBt@Y76$MOVj#^`4&|0o5mk9>*$IX;Z~2@e2Suj z8Jn!7N6rcn&i#M{dMvl&Wjma(p+MK_xS*zzf-lAdQRYMT)x%jgskawC(vDZ;x_?6W zLqg#$H9C+xQ1Jn|Ek29xez_Gfo{{@5@_c_!9EbkP!MP%N68}#%XSo0Mw?I3&0QvRr z-;OWN;UhfYJrE9LHc-`Q;{tG5;B+pj6qqvV3Q1iziTi{MgX6w$4&i(I$l@aLeWIEv z75ZMDSfpUBra|9F_=Cu55%iflsEC)hz|AUr!YDpRlk^r{?P*fDgur9IW>S#Ggl;Q6 z)@w0g0Kych=qIiE=Wv9>aL3zLlx*yRE@pLN!>hfW5QGCvK9pW6Z$SUqVy#d^uD7Bd zrdU1erKDJf_DV(AxeS} z)~9XLCN*wTNBtG&$%2Z~qBap<#`s8ks_v`6H=+SgKYus8f%~=cPn-YGrD}-Gq)T`J z^9crXFMRATLkh%?f%*N1nqB=Bb{=Y1krYS}KmigGL4BXcz8o-w3wm1=l?iKh z{F@H;w9ETfZbJ7R73juC7%kb3h0Do;LE#ec>|{FFI+&gf#wb}eTOBZP+F{K8eQSr32JLGy+d-Y3N}ITrO09w0n-19* znUe>{KuJp_?}R#ndx^yS#KmG3Yb zHSjR0J{UcMJ+PmJK4&6N0bWyThUl<{x{$P|jJO^5vl}wKTfySF2uXiFsb@b;s$yZ3 zjryPLPYFFxA{syR?rLvvJe;81J?rlw7&BhrT&2!nwc|Y__Qylk{Ut&mwK2lu%)9ez?rQCd5URB3~n&VMp?MF*=iCNU6ew3`pg1O8@yltTM<*F9-+3 z9@;H_IiY+!L+`Um;%nV2Oqh8*?WHOu627ij8(Ga&)|)=hWQ-R(j`5#J5LYT-%jEWW zL16+ne6Zoa?rEY2q}AkTlz9XZ5J7`dwm-xPyCU>q0DE-~cb|jJMGp;N=Dom48%<=t zOuxjMQI(YL7g9>|HF}&8^1M%WBAVLBFPJD{>6Ro>c-NGID-s?NKskO%DGpDqh$K;= zlqR&uVY!g^%W#5G@NOop4O|~vTGyebmg5ElM}IERpn<<%o`HCzu-R7?`<*rM4rU@0 zcTODK_nYJ?NMR|e6j;!NOR{q${E;g0YIg|znp833dF=8GtLvp&oYzD(zc@;A%&H}8kb@}Je3q0xphuNsU zKWm7h>@rCIhnZ_lg3jNMM?IrGKs4|X%E;Vb5_FXjokG)Tp~41H{d=^zn3!Ujh%KCo z&0#6Mh7p|I8tB;c8E{FONE3+``E^wCOeCRR$FR6-GbSS|RJRd6BmUOu+S_&|j6`ec zT&OIHe}^mo79yub3p1WR+B8EOTT~w5(PaA=*9!@u!tGql!f(}aKjAi9Vwry#D2_dz zOb&l2Q9tCYnVVn#kmo}T<3vMaQ6hJ;kDVm)HSz5{wJdDlJtx_nifSOjUfI&dL3s1?$j-3Ad2!Ejxm(S++F@0cAp$*L^?2^VG?(GK} zjF+?sJ(O{vVJc+_V0$eiK|*y2wmz`gx1 zPl_KSJR2SC3T3qq02RIp@CXzY;IbCee+-5o9$S^Odot5?Yn0w=NF)JWsgkm2T`6kIJ9rWXNIbAO8ekLy8&}5BAuGVvwcWE3wC=Q=(B$?2v zI@;O0MC!y$hg!&fez#`tPT)AAL&49Lx8$!s*R$f9F!628mupQUa9ixv8^!lZeG5Lyra`;_o5=??-Av841f*ASj1MnN!d{poaQ~W zi9okedyTq){lSx0Oh33t71=8&2@ccUj_m_FvA4_o_r!EEAz})*{X$hH)qb~>9e7f= zK4m_aL&dWfX!sa0I%mzlb-@RuO^cku5pg4O98Fb|K*84Wo@!$Y@*B}M9MYWb^5FV;)*zgJOLRA?6fg}j9F^qKlQJZw#LJe*GF zz`7kw6^fjE^vd7p%|>Zl_0p{D~ zeQwsuni^BEr{x*PU?{6O6KY1)9PjaWAdqbNuC8VK^gR_hvBGw`C3WZE@MSB#J>Kq5 z9o%oke6(#GMk4z&VD-;VD+3FJ8aYr#28QGK-c+Q;1fWGtj?xi`+w-Ezu){8srJ&9h z_pq=S1CBP3^rRsFr)1VKpf<)voA2f>XhU+_?W_k1j#Fm|H?{oDAiHl8iHJ8 zhrOHATCsn5Q+Pgb(OR!xq>x4Y&EHRa-l4NSZBD&?sS~m}asf9{sw&m$af%0!b8Gu1 z!zra3M6J!_T*nuV6C%MU2#=PZ-k37HPiNmV?=JWC6VXhJe z$-Sx2$=VFsK&0l3p1)VUSs!?$d-sT#$x^(~*NZaj(Z&TIe6pDBn|I>QF9yk%eC|H; z+L25f&XhFv5LrC3*?#}Q-4pZztyf)B@7L$kk;|LLQ7*LU&XT+EZe0^<;nn*+fltX* zM;~p%$YV%~jgt$fcNZ$ZgIM`|^5vRNQ(SHOzHr$XeU6M>VQvKT9lq1BA{lGhi9R2j z_+cc&x1#XNaxM=~9gVF|>StYd7i|QqFsonCCx&W(PL>;w4B@(z%5vExnUIMmZ=ebF z7v46wP#=&y$v0BhzRRc0>F|M+Q+MG=rCgj50c1L>zW>3$FrAL(|S;ySUj14 zXtn-Ps2poM)giE_=(N_u5R9s)>6?w%UuzNNrhkAM9gGIq1`Yn*`cC;wH{yCR-6yB5 zwP!NPj?0e#?(oOJ$d`;o)v;?(_tmR(xdom=R2v~S z!1Mk98Wzj7$R-F!qo-Po&9SUXX}4Gpo?TdGXVI}5BEKe?DiX>6{+^oMp*x9fY3OrP zl6h@qLG2DlvN;p7#p<<$$LZ7i+Yr_AiE^nUN9g-@TeFwUwr28P=`m#}+ih2VR*Y+% z3?o-xZK}w^(C^H&!Dhst#>dCR7^*1aR)=H0@w?ilEDXshmffa}9yH)Y{RZOhg+wZA zRs(t~APtI&_tjRxOw0`g70j8Z`Pu<~ z)B(TxMQhLRrkC44EmkVY2l=<^w|g&$`-ew( zTc3IUU!_XmZZOQRVC_2dQ0?1}ml~5kM>_Dw@eC&>NZQ>G+grW83|wdXGtJIz9T1b_ zcmA!wO&bC{w19@o!PJ5V`H2!_lQh|O^6jZW#~ zyh(5GL@w6CJAb#B5&x4Vi)uNSF!sYb}l`alzkW!neqPxCgyBI8j-3+#f z-X`!}#9x|msNa&R`1sOnR*sflq^=vWJ$+0Y2P}A86KvPK4(EMs>Jc+FBRORx<{t=ucZYAZN|7aU_#T#8^nA3b0(AgKz_}&NRFJzTN2gJpDpL5N z$4ShGYmck51Cms8qw&=9e(#%VeMZIW`#$9ElSXnvKtc7jZn72LL;Hq9Q3z*%SSI*q zRz~9ca*{x>9n{YG)>M#4NU+VMKnajX@?!z>x7@>BV?7ofpuW9ro>wQVQRAUNJQ5|h zQ~vJye;B7OCn=asfJ%2j17?+u-h|>6A8d_#6D|DVS{KNd{gUefyWXEdwnU*pHlkyO zG(XP=l~>?h_b0mbLu)L=jh36@djJFrbOn!eJAMGjQrVKY>vK=QOZF?TOIA$IUIF$? z>ikS;&guTTeLpXIuj-0gG6vm!?Wy_=trdfZLW!(jhov7xo~ALAGJ)LpYSgGV7!#Eo zos{_7-E9BHQM^#3t5rscRv7eCZ*&bI?B<*KRtwP#ja=u}C`i>;*?AJe3^NK6`*{l@ z@lvup>&{*M><51puGgzj9aK*^cNl{J3^SNSKy^i$3GX(ONZU^qc$FY@cphq4-BggW zfW0t{4>F)Mm@o8=knDdRIZN!f=E!;l3V}J~@q&9x=!))73{;%d!v#pV-~!k^?+VfT z-(ah;dLPg1)hj%$NA!f9?mS zJI9*MLJ`kOw2AMI>&AOtdw0q=o~~@kVnxtv==t2t_CYksG#uFQR|A0<`MArP*rnZ0 z0+Ar{VoRvj$3bo|eBoBJvtdrZb@4F6*!KbOl+1Hp&1F4KRXyx&F%Nh;0-{2Vhrh-( zIPLULc7B%aNvve}m~62buEX>i6i;BU(Q>*Sx8}AciY{RgDjgjb{ZWDplKC?E7_r)} zE~X{^<}j9kOT3H|h`PDAsV31_Io=svQYYD~(B_DRxpU~V560k*C|CD5zDSe`lVDo& zJ$pJoLwY84Tk_R>D7V;yM5K?6C$J&MH!5q=tqYh9|GMoD&)`CKE&y$sb&z4Ub4S6q9@tDA?bCc#q!tEt=;x69{1coDYu?V&oE^M}J-4;pon zir`2Yt~7B~HxTgIbWr`61eZz^lR5NopXJcMp|Vxu_JPz5b7?O!eBs7J)HZ!;cMT`) z?aF?9joz}~!**RjE}p!&h zs@O3wI&K~!;?sICQu=IFhvf2gMeTs~josYqTTQ*2;w}SZzsuxl=Xjn}R0L(`&B(zp z#_F~=y1qBAx9l4nvR|s|y%d6g8(jh7@i=If%(*2*M!r;lQ?`pr5z-5%llQ~mfWi#C z>=a!y!onAhVjy4RPUK5!zM!)FCoCotBKL*l$|LOcb+YFM*JQml(eRj&#u%#bytP#0 z^Cr6C0IQgUlY--uKMXu&L-}pD$1)xFbJrr-gkJ}MJ|*-xscAeRc{=xPNDaIkkHBia z{thwT9bqAPNn4R16HflTqP146nC-aWz|x3k=N%@bd-LG2+PdFku;`t0TAM1g6kaQ7 zd2#y1|M9A-W+RNGz{u$?JImR=&v>dYQgY@4a%byi<3+h1;o}CDW;n_7WBR$g_Y86c zh45+f?`$J9>R2A6SAJS^@uOTzoR0^Jzrq67?$Zi22ge)ypKelWh8+DM8%D}P&b!AD zs>Al<{`hvUO7aH@^p2Oew@dk3FOOX3v>B!5D>|ROU^`uX$y}D3uzLGsl6%(SZ+o+z zB#*+V<9PUY@31PUDa~Kll zL+y7{YZ{zdV|+aS+dAf}RP){OEr8kMYv?t2KT_lJR_dw;8{4Tj*3*}NW9fXvx`@xO z*aoEDV(AZ>nvBQsvW44)jMeBkC(}CxYY%76=4pwX4Gr`=<&<9bexO}8+vWR#*Q%E( zs>pTtp`%>;*qDShl3MLL;Wav)ryG%zbv;ce%NpF3O!4khYVC~?V9?T=s8NBcN>Rxc#GknH9`}GvHySUxGUfYMRvYGD*a# z>Zlmvq|_9=2zrZeFCl8hGbjs6Qu>0-Bz7`paykJZ;d+@snUnL-0wIJhWy#|s( z4c7f3Acs1x+}j#)n^Su|3Xhc{HB~@L`gSw*4Yd^ntszad=&jt2(4%9U0JXOcWQTQ} z(QL}_317z+G!W;zX3sgY&|v3kn5x(5r^Cgw8?~CCPDSo}{b`|hdjzKa;hKKQ&y)Bv zw7M6IpXntTwNxBBYumZXyj3wNpzv@Yjdv$=-<;k=KC^BHc*HV+Qv{jdik8E&+sHdT zlW*xnf=}0Vx0~&;n*|o?I=LK;CWBgo{yu@RolX@Ew;z=?wZ@1(?xqtw@d}nXk1%sD zbu7MlNf=J*B_7i-F6bR@@O1dg$WEu(`N)oYkl;Da%Q;2fEe_SE$LDI4JHGtG^F=)0 zW)vavk)GQsbCPWFw|gP0&$@m+nj*kJ1uAE)^4Xjg!u& zvolG@1dlS$&#AFOL9DS64?_{@1`&XgNTUP2L7k(5|Ng{*6>adU6$H2Te0!Ul^|TS7lfy8a~&BiK05vM0X`XHaxb)FSTCJ!70 zR;-X11wNa?K8T;I02yq%ui`{=6Ugi;Z5^%n2|J4QH1NC6a9D;)e>$fBrQO0;mDMa; zfY?LsMiGTU;l)5FH52D5sP9H130BrW>HA=nBU@@Ax0S3&6RW6q{X!lL=@5@?(V>)?Qm> zjVta|!iJennNs0kq%+cIGlwU|P?{`YsD{4}?bt_~N>IncikKKBeH~D%DwgNZrZ~s& z4%r2ca-%wCw6#wI_W z?r%oFh{Op`37=l3mtOZb5?+X!T~|0UYQ)JtU~OK_YD$Ggv$RpXytRCT=vHWf2S6MHwe3NT<1@` zg#S>j(`XO=zMr5wE4tL!l3~C0bQ>8$uy+iUZTS5bpZby&h8AYiJl6*98WM%VeXeqd z%@agcPFTa7Pih&+>`1szdm%LC|E>FIog$xC)t=rC#+S#GY$UGw{$44z%q9b;mc=i~ zg#rmZ)5DfhbsiUEofD9%)<2|kmy2|bc!gv6vm~+WlH5@-Fd`nYrTczqQ)lYCaV z!CWJvRON;vW^WV?aLs>e<7ynJeF43bocQpqmM4?H#D>TCfo0=1qY6WgVKMy1%Z?Dx zA!VFw;SPp5V=cV-nO#jA__e5UWGjo)(~I&F4X3&(~}u zvj-?ko|gx%OP!2FjqChO*YHq7c0vrS*l4F6)l&)DZMzza8X?Xo8-LunZ{GXHIwHV* z4}CgAyT}5Wy|YBf*RgLNNprKu9~1cfq4mcK)3V!=5OMd>@K&C~nr-Uj2rB_JN+GHb z*?WWh_!eIW9FrD<)x(nImv!v{4;!poyl^nZ49%HpNPZmu5D?fcvfC zOoPqScovgU)WehICIPEIM5-cA=(NxATx1P}dllmDeQ{bhSz#S%S^EYISE#D6R;oJ4 zwx)qaWuWjGTl09EfqA-J2cLl5+5}LQv>kz)P?MB8xH z_J11*!dJooV1GX!H8FZTxi~KC7C|uP(6UgZFp(l`8BTg>AoW4sBd|l`yCBr(;`f|w z=TktvO#Uvd{8!uZ)wS3q97X;tZ!5=q#M0urclUcJ>62jV`>+n3a3P0gb&rFuHb98z zw`Owl`LM9NNpWe0L%03s$;eW!e0bFP1YccG^x0^n^J{Nj{+I7;+^f+CPwTBe^X`nC zUmoS3>@3iR^kVY8u5gG?>SVthH8eQXPkd38qkV}r$9k_p098J6P#u#cPNYj5D!+X5 zy+Dh!75-*ES}Bk|w~j%FYj;vvO2|~qd!aOr-7_SmmENV#NX}iPp;{KD_L?dWF$qzF z*f>av=4(D6exzv(Dk?=G9_R!VW%DB<*2HqGpB2cVen*r3pNH6c1~8V|o>5xkXvu_l|gbc4Dxq6g)HQ zd@J`t{#<;*D9wNSdyMa_j{p)Npu7gh!r?vL1 zgCy(vaF(%1hp4p2#Q1sxXdz&AHmy?~?d4tw2oW-U??`{C0Y3k{45)6CIKDU#QhlUJ zDW^;F9M_oq&>&R+UMRXxr~8bc@Rf5iZR9M$reTcNX7KTsfnx00%VBko{~17&qfL2U zmDP)NH`Yz_&^NLhz6uroai7|rXVA&KIXv&e(knX~FGx%-STop6Dd+Lnq@*P;w^WoA z&C)>vWY-*wQib-S^(r%zYT*d7CuKl&Z};Roa>RlXoC1numO z!L|($J#TzH=@DgYbf|l+2DT>!gtW9wpSgdn{&okNM@u0n24Sq75dP=f^8EJvditF| z^+!cUA9algKyI~is_d_wW@jf^vCIuigyPu*dx4em?n0bZS>LT-lUhxR@6+p#OhKf) zwlx+wBBl%0X!0sTGo_ikv)nEc5~Oq=wF~=D#-?H5LV0J!GU;*v2HeP#bI)ZmTc0xqH!Yw2 z?u^3QWj~6ss-AWD2F3SyVGlaKs@QU3vEnvT={9-}y4_Y)3=x{b^G{iL**~w1KL##TFQR&$YS~bu zJI?axkpZ7|JRSQ}maq?Ie{YENenZv;JQ|sg0coRY>_c&oAuM;udRHHYA>*s1J)Re6 zKe;T|1ixYTf7-_4KewMZ=hA%rc@yPBpzTNgiW+)}DPh!h$fn#2&B+?O6W}KbPZU9) zOvq1^|0y_C0YUyRg2wVQa?QKXsVO7Ojk>Sa_ck2XCK#*Gg`P9CI{hB{W;qf#f4#`6 zHEP)FLDPKHv-|AM;Vap{_WY-R`zYA@2J!K1dM{vE7!iY)Hr*oBvt>9_@Vb)GC>p1H zDLU&^P1CQuZi$H>a_I9E>o)GTw7|Wg6nR+SI{z__#rIH*0rEw7sNT}N+UGL;hV(vG zZ?QGCvL14LJ02nv`2O=Nza@S=h3-cK9;VH?CcSTK3jF*}^fRNi(kn@v)6{R|oe~fn zW}?j2R6P(pVBSDCsa3`T+9F^t@Tqjvuzm?=H6b0=J?+Z@QJd5=-E=rE3%s(Pb#Y$d z5OgPj6CJy^RQXVCP8J`AN>QLk_#K;yEyYwW6W^iO`&o%_cJAR&dGV*A>9}?BoSJ@` zm4r%xz>*CQO^s>nwOo0#^YO(iHoLUvhqcBn6#5&93CnsChgWdzp|sc3lFNkg8z--f zB>n z`9qs8qsn^^1aRPB8~rPnK@a%`-X}LnV4kmEwdfltZEIy-7{jvf0G{qG++h`Vnp!oX zMoCTEJ)pP8xX^MNDXp%(w{}}=|J{$r`Qf?MW#LVuuk{BWn#f8i z$tj`rJ1L&`2POG&kvbU^#4O~zuV0ex167WWTd9b5&das5eQq^`P75>_^@x|P$1TaE z{>zb*Oyz1^iV;Xu>v4{pk&LAxoc3UPO2 zlwGW_$=+ux+-VoUsj=9NNYLjyAq>8^Uvrl7-rqoi37apw3y}Xura+xTB?E_hb8=8UKQJy1-v)P>~QEFi}t& zn1_58=zU6L^*dCIW09pn{MCGph((y{Be=FouXTXX37bb@3fFIR&cL6Pgf!?5hpp+= z!Gw`&{`gNiXN0`!qgd|6tqqvacyix73Iw^(EEpYKpVHCEGWG7;mJ-t`62FuM9*wb| zZ=(fny0gJgp$3Z^OH7u0joH*cJ9`j3=I|4>s8=bYahf2N{nmvoX$PR>H`=6cg2Kqd zbg6Wk?Ci+|1JDSYhvc|TP4t0@W{4M9`&6>CSOT{Bnh{W8tZo+G{bVV+VmD*6T&QV* zQpVv#;wP2i7OPhx*D%PX*PJkjeZU74u+*Eqj%DIg*a`C3J&#AYpKC*F+{pc@EG@+* z435t_2Un7_eER3C9^=h4P@=hG=d2s~ejdE~Va48H)9z*SAx(?9S7n&BCklR`C&Xo1 zo*Pw%h-+A0p}djZu2SmNCsB$ijqk3%^caaZKX>Z6Exfh8(|R3qVAFa~v_uuGxHZDs zi63X@GdT8s@7l_FQQG^;YgXJipph~?l)AaI{u|}_Q9(@156 zK-;o)+X`2z%s<4u^(O6yn?NI)h~W_nqC)O?%i%!W6AMCNhX8-+Ar6%AdtS-JFxIoy zrNDI`PCk{nj@vSO&o^cQZ)S)|pGBllM`(zfMm8?0$4>^b0i6m5O7Gd(r@KA!4Sfqn z$Wu5um?J7g_9OsS=W_^_$=FMp{YU(7QDgW)M6#?6Z<#$EEf?#x3HkgP{-U0U+2JZy z+xL5-#g-d9dk;t0=rNY6JSPIMRYRxo_t48`va>o_E-t zY}P4&i+eRH-}jbTxIldSWw+#d9d0$yev#+mBB^6RFG!pK^Ex8d^(A5PK*m;){f6cw zaYLEGsA>RfDA9U7EjZjOA)U@IxQ{gsBO*rH;)pvF-Akd~es3`%QrI-4^a&`WBGB?o z1W3#&X$aCNsXS0A)=-i%gEl2grLwtHSdNFmwj1QWA ztZ>TaZsKE5K!0&u@I3?RUH;N6cvj_%eT1; z4sE_=L%iF`%$&xmD;$abtP^T9@AqgNV1>hW(y3@RWtVsb=(c;3vh=dAYTnN#PLt<>Y64lvg=XvS;Rk1vH%^rhv%~> zE1u5Y_g*AKzn2hND-3LwT-)E=_Co#Q!`&@E!5zS%F`3qdzzdjj28LL#wrT%%G+2WX z+j)xgfu-BAq8bks{@(R2!jKXV2{v8{)@eu$)%99k=n_VDIJ&El_nzu{Lv$il&*jHf zw&1L6KcjA?tV^5-(ZH8`D;O;$K3A#_oY;uWRAGS!D|xp{coN%aFYXenhDcn_Q^Coa z?@hN3iHGe5VoCfBJXrr=pu(^SMPUW>hUP_;#w0jzjU(z*tYzp;-v-()f%W)npw4{) zy;hz@FD4z9|3b=3Xc5fKlZw7kxN*k`XHjEAXf-6orhM>Nka1adpA*QpK`K1(Aa*vm z`e=&4Y>IeHPZk&H#Ci-a8*@>MYT^9pnYom- zJa|9yV560c5)S1^fbWj8Zc2jHG+M7q_j-iDrRn1dvD-0^6W~7J>pafxZZZN3^kS4(3ae@E2ot9 zGp#~F#=oef){!~Q5g5e2aB4kM8tTMAv9pzva-?OFjjQn#nqE^WvyCy}FDMBFDAVqJ z6u>ES+K#n7M4TZYUFcOPlOlOpT9=Tv)2YTbZaE2cMle@FAU=G1$#F2U(RlJ-otSG# z0CcyM+Hj?UT4@{^JGn@ukCKz_nKGu55DGiqEt!vF_N{jr&qt^PaxXox7* z<$v|lkhNrO>_c?d_75eP_opzJxgr->M*Qb^+RwpnqxO_^N*I`E@I<*|a*<%RUFq>- zUa63S%4bkya1SlTq-*=T%xFzbzEvt-Qu5U&ru#4CIcPsWnbJ}Ku{0`mmu%hp-Os&` z2P#HBZF7HFm?Hu)H{9dEMq^xcmBVs%e*7v38%{oB;2p{4%RUmNvK>127QwTlmqb`fud1=2tyBMr^)I#gEib`HZ3}QHOt_ z?g8^~LFTl2x%V3*%^x`+Y8pm|2Llq5k}@%G#Y&ezRwynhJ}fOn^C2ZXfHIzi9)60V zHlN*dLEXC~>cm=AT=hT%rm*EwrpyNqD*tt2`(I^^u|$ExCkhgK(2dnTqAuzgKB!Wx ze>aPt*bYN!+NPS7G=r(QaEVO0Uddh_ldu}Tb3sbS{yISy!Yf}&BkarX60MJUitXaO zmxb)~uDUpDo_ow0Y$8Rov#S4U_aBfs9Ta$$hS1p7EPg8t6GgaBRFS!Ckgp@JQ_Luk zlj`DrfB!7vgXl`-0k9uAoe(6I1&yp@Io6Wx0b1X}db|`TX<{NHC1h@KeM6-|iW0H( zXm%PP5Ps z548Hj20_WlB~~tjR7DqQgej#&O?ZK?YAf&ooRo{iD%4yuXu&0`P%gv$Yi0%~6*Wpu z%PyWS5J4WSdI|$#5~c!to?cEOT$nV8tq~IWb}4OjMwjHrZy%xEG5_kKe!?Jkk#ed% zQ|<^67JCp}kk9sD8L7ycVoPnmjZwn?6uecRnMA)ofki6*G~1X54vI;{I2<|(+gJQ@ zF@>ef=Xp4**^xSR`tL89JW%uz-kqjOD9Z{bp<9DCvaY~F~! zW#$Z{!3iUbh>ENVX^d!&aLsxMKLWbhHHCS}FPM^Iy#h2(@sr_lvWJLcz~wDkA9p5M z^~U|{d*M_;VA%V;iAMYiifza|;E(*NDZyF-LE2RoCSXWgwj_eHbfSbp&9YeEt3Gm` zQjvseQ?ei~f@HB-tqKDQasIp0{`)QkN=6Cq$P&kr3FcE?VueDmVw+7rV#*{Zz2Vt< z8JWla*0j&~N9m&UMemD^q_kRU_HeghYub20`t+e!i|TL2)o1*7CD6~q$CyuBf|NO~%BK%kQ1 z7qxWh&q}G(`kc}S`S8tDa7cQ`hUBCwixY`Y39){~146$4etbaGO3kW8lvh}QbM=F_ zhbIdGC&3Y^$g+hfvxKncDD8&Oq)L2OsuoOOpwboP;-(AydS8s`eVtf+Em@O~bk4?NQKlpJ}yo>t_# z?~OkOCf$LtB>gR~#<{Z_Y#KkLF4+o>FwHq_q|I4xgxaMgIP72EE}hMj`#*mn*N?-aU-xb90GK*Hl)bvfX{!7w79Gem}j^5yRE2L2?cH6lHxe>>8)M~0e*PjS;aQEn=%|Is4UM= z%&TDENX(UzL}DwMe3szDfvzlD3hyuZ>G)@B)oNQM0LcKahmE@JAKmcqj4!8ySb zNMGLR?klBXmM+37>`1`LaImM4v_0a!F}JB)^7}flU~wjJ60#NdV*zqqH(t@Jk>$J- z%Vq08UDeE>`7Mz>-QZJdmhb7^McP+x2YbbMuNi#%1t-at{x7Hc_ANUae9wJ>%@2Z` z=VB)TPrpY~E$V-YVF?){y>hW!XzUmF>b)hIkK?$WC$(@%8!J^)^j0F>x2p1nu_d_g8zMN)?-m5bwsmKTPH|gh9Q{ zn9Y?pMFYny|K%C>Hu0KwgZySoK=MU-zpUchfl;vHjOL z;h~Pd>MnmqZJ|2-q(gl(kZQb4ORj~+NZk1prJ0Sx@o`*7VEc@6>+9ekdbs>n_Ox0} z*Lr*XF@SF`0zEnlfc~&-9V{H#99#Qp8*Z3b2?hko1AqY7oYeoScJ;P9Bu~(y5&g6+ zNtsV5Ay8zA9PYVI&baz-U;rp~JJC(%9TLPZ1J2PJA_ayo&>GISbG)xM=%$J){5NG9 zbYO%Ir^Ip?4TVHSmMEp7?v+>a&GH~zf#UXMv$;D7-mt+_#qJSUDDPk8g|JvGw~&vv zCeA69PP~3E)Jne;5@2YP8gK&rh~B+ zotBEOPn(hBpX}+S%yJxmPPN>*~n?`pTdkf*K;653k-?^h`2mmcY_ID0~F|tAaS5CFCU?Ez-m3qL?XZzc)8?ckzRN zT|qx$!+gxJ=Mi{WEGSp_hR}Sp39o=Orud-rZ!Z8S7-LJR&8((XyGs40(X`Ex`3-3z zVx`Il0qwoF|15J3v4W^FWhHh=`#mBcfosDT_)?~fmejF2lo+&y=(N237(ByC`Q@(@ z^X1FDmk@#O=7o*4vDcxdmeI{wJPDrvTR4TU4`1Hbg|DakXIhCC!1m6#$G!4D?YOm{ z*_H**ATr|9CXW+6+~pd@SzSUb_?ATj5)usF9f1y4a^nmF@z&2v5$(YW%h{9EM}z^D{Lgo^!E;~aKdY;1HQevzbme|>OHl~@g*b#8aDBbt^w`sWIc^(c zS%jnA08Hmw)t%rO9sw!)Qb~^AC;3qGWo$EjEuvO4wIe_zZvg}9cQ-H(Eg7C|BQuCXT z+aDKWtonP>(k$C^Flq$}rowJBiYs=T_B&n zAoN!0n7$E*VWrx-zE~>00dk*ed{o)=3(VSi$Kk904&kmTdZ8ivSHI=umx}tD`;stB3nbTXgS|&TUagu^V45Iy0k+Qjn)e>YCt0|OGh%Gy%jlkfqZ|r7D_g- zHZ;k3D@WL#AkaxQhlQ|DFK1Zul zCOs@-BuoaSei$pO{-QPRS+23Cxh{$Nwt^@T5r3JxIU&|d?W#pL)IF5rS*B4^2Ks#t zkK{w)9H@d?c7G~|<5o=I8RJ07J-8Su!TF^N5)^JXydz<*0Hr87A3uW1&hZdVnb79` z>;3#2bzxqK>h|TTuJhu%5T0_Fmbzd3zZVZmP{%Vw^E~F*hgPvC}VYu_8{Y-%=J_5=QJ-)><5N z`jJ*5^@V9gi*P5^*lV7QJ)!Z34Zc(SSNjE)(;uCXjc}sGIg6ESuUe^-=4%>rMkTik z6JEqjtOBZxPj!igE}H(xSeq%xogX4ry&s7+wKUlhcS~K(YUh#{cS=vxl>{ii;P`^o zHe6a3&7$?1Sh9|5Bv2V-I%i9sX)^X&5AJMfuER;a8fu9ZK7=Hbi7E-7HwT~gF*luH zMg)r#rpxnH(qFs0@L9es%nBu&pm4&!9U^TnC}yBHZ?jqCc3(O6(91hThcApH2G=Ah z@m7uATHu(wsnqs7{9d|Qxvp6`ZD+Lf+xA1#ke_ip)i!WzT*XydWeg=PJY*5Lt@HQu zA!K((99frVsV}d7n{7+rd>;10`gi4#h-Hp#pR@rf!kU5kuw8?xkHB69=f$w)pzm(L z1Sq)tDHY2&+~lV=3L(j_-yXQgj{5PIEoyofXQCH`F!YIvLiw=<4gKW3l z)v`wM=JZNTy${HiOj7m=Rf(~2P9ibx!B%h| z8#^ZT%hmf4TTx^c3%hk}(N*h0d&?i#TGSQ#TfkVK&}Fo|^?j!vLo2-x3LBZgAo zhs*K;6T9wj+!*l=P{~hCrSdO-Yd7S*`@(=lytFrB4PgrsdHi{ago(0Wd$^N0MABs6 zw8kF)Dj~L2ft=BO)!aeAek$z^0)a1Qq&Y|DO12Lkbi)A&_{K4jLfsRl1l<2}?7fb{lYk6{L<->g6a zf!~HT@_BY!X`Adk|24|N3r$zcPlKa{R)_f73`d+YMY?aVDp~Nss#7WcWc9MOhQj7C z%e~RIx-Ab;EQI0%W%a>LhkN9G`@}qUM36X;q2^Bw^Yc5c)&Ah#7aRc~*_Epu?#0mY z!gyHl19eW9WsXnh`Yo=`xv=2j;uexy^=vETv!={y(+W8{L0^=AcnxN#Xr}%QPQM!2*-(C?fva?kE6YF^zmBPwC|Sgbd}JdlY8aJ;_p(S zQ~(BO1#`WSQ~Tjl`i8Rrot^w(XFiIVH+~oRc!?B*$gegfE0W-GP$D?IuA1pw5{GE~ z+$0XM%JPTdtYe=zO`Sj_ueGEKm6?hW3)m=P==8%njyXapcSQnT=Pu%pg*L}@CwfT_Ft+(H$jo5tx zsTbrsd2&3p~AgX3036c8^h3bFrz+lH(y(0s7awhaf5i#x8u`Z+yvq0 zn|ff7!d&Jk5(W;aRjiooe8kJ#$E#oc_FDu=-&1YFkJDi>__N&tWsZrdo4Q+Xs0)DG z>}lK`c^oa0Ykv+j9n&VS3&hd%g{897;{VQ zz5vN!vu7V}Pu3WIJU!E$_Q}v>uE?ir=)Jh9IowLK%o*b>IVECKd?R2(X^#sIo!21i z@K|<3GbglUSZ|(*V$@hN8|0xJu6_4%`dy>L=)yK!)BNCv;xAge<8j0lY4Qy6K=ln5 zIWk{+Z8gF*>JInSRkB<8kq|Ty8=#(6)qsY$3LXNBESiPH%wtuE19Pry?>Wi8shs2@ zOp`B+ibROSh_n)4!R>D4o1b#v2m&n%ayeYJyr$4Uj_k%5QI0Ole6;YX+`Di#5`TPW zoJ|pNw`~!bXgNMk!%sx7n$d&EzetUzzy`;`QWZyVY$JlFr0ITt@$*M0kk_@p5>pEx zc<08Xo*zSEfv}aPvp>{~P2h~Up*n%4y$XG+Yq!+widZCwq1c3X-siF|JGAmK!|qGQ zxP%sPh|m{@B77J2^WICuh!;68Jl|IBEqp4=ldwA|`l^;9#%3UVHG0RDEJve@XW&|E z=r>@Y8cu!uB}LmwNxx#X23&n9pf(YHm!-Y3_g&jW!I^pdvH=mS|-nXV+BFbyj8FuZ7*K5_r z$ZX}2Z?HNjP+l_*==6=K^(<;4Z=pQk`j23ldS_0cgNL}eN)jvezDA;i`QdLaN*oq6l zD9X*$MTOluG1vtVIWCg7@RIiTn66MU+TUf$8iFu;8pikKJL`i@R}_O)E|k^sP)$i`U_%rf`&><^H(A# zhRx!c0x#D|oLrmlpi8C--PcPr>7XPb=KL;* z<708SP6o$bEbaPrBYs;j3ZSgg+jNPpaXuTbpNPH9ULM=MHwyYDw6l$6AARPBWOPN$ z^R(<3I}BR$ykvG?5TZ0sv#<5L{lz#)vU$45Slvl5++A>~#Vvd{)d#-KZrgiE+PEec zb(Vt?7p?mqcj|41EcPG*d5S)Fcth15dTI?esucpFH2-rIw;>H0y%)Ol-+erT-9(`4 z<1QttOQaNq+kmTkP)P+|DvUyjjEBT<+XU7-xPuWqdhm2nEC=Wz%#JCE;zC<>^eH}1 ztv8l~jHvJdzc?=XwIe&-s-$~h9(@yzTu5&=%PMkcv|Zw+G0(K7@qKFNgN*hI5YZly|FNEH`rlZsR|Mz!MPcWx7l_noX#J z6-z3eTYvuE(Kbq9OuFwHpS3%aMIdnfhHvEB)8f_tg+{-DQXxBXmGbp(MpjZYeY;NG zPtN01DF`z~A*>OM8HHn1ZXZ6Qn+9igqM{4q!2p2{QZOjbx_K@D-mzUt7x;mZx+3_k z|Fy~rVE5h30N49I2}9>t%rY~@GT0N>GKY^&dTMI;gZ%<}Jk+>a%TW~LtXYrXxhe|+ zuSqHnGQ3y{D?fy2rDY*mmg^e*eY-nnU5)16&&v|@jGwkcsKgha_}bEU6UW_)HSVW} za%7^x_P6v-T4dCb-xCq5p--#1&>2r#l9UC1xi33~m+IMs&DE1D#YEuoJN929Y|Jre zok!YHtb%>C1!yE%i(p*%tGFh&z5BVfbyFv@D8wWPbG(iW(-V5`#3Y{iZ=KTCR*|)> zH0smvoUbC9`W??}Z~L9r?jWnt;FukV#>nGCsPPQZAv_H5T-!TeiFwd&J!OAAl5SSc z@h~i1&;4Tw$BFKo`s}_8>_QY?SxKy84%@1codjuIZ({Y6DlkG+;vnmt&1}uiZ}Q*6 z!231Re-e$sv^e5M!}D%+lF7tQR*4Bl?TXot&y6R!hJQ{T0za9P(vJny5tZeqLUO{p zqc-|;pKn)bjnKIiI-?PkJ=Mtr?HB?eLOZV1hvXlkMgsd#xn(dhs1K+rb#kmhUGHF8 z6~bt0ib8ud!8=F27+QmaYk($xaw)gNQ4QbuYRIDmrMU}oa?c1}PQF25B+}AtIlJ>r zAGhYk1Or!`_4c1Iiq~=;t6nAT^Rjk~bPciZ9~915hqEE#^03gbbebuZC8K8m;XqWYtMfkz$DQx6EET!PrZ`&E!lxcnDz@$HL^JDz%dq- z`S>aG-G}C%S}alRaDM5uOw++ja{Z0NZ|tx6+)t8)6)(bb>Zg`>z`_6}VReFY?FOhKbrrmuhh22Y1=QsbtL;*r?rJ|Q|9mda9dvsPNU zh4z;Zn(&oO_OqR7-_37ADtq!Ju>JzWJRGT~=&wg3L;%E8^YpAKj5-(kZtV>M_WI5S z{4-J&E_9)7K}o%b)k~l+zFj#r%DMaI@2d104GcrTLKM8Uliy2^0ihNEgSwKnHr#tpE-}T zGPoce(r+Ou6Yqmm2ftz@7){Zt)Y}2QBqE%QjPM`$`)cJhpovtZRak}rsI$MLrJHTR zcqBLI2t20brK~>7b5SwTd~qRv8LW2^s-+ydfXGfTLtAMXYLB^>Pf?Cny&HoF%c zXB7&MPW3mN3gbHa6<&}gn*x~h3|puCgXhJ>k9_AMH%~=%v%+Ae3`}h*TUvoU#T8QT zyXgjeZAy!r&JsiuKHdZwYo=`1{G`J#mfWgt`VTriJ?bvtVwuG+d9-U~DDn{&AbN=BS~F`3_bR^>kYTZ*gBN~1GH>-C*3-+&Re8@19wI& z^A9TsJvA6wdbOK~?P{h{VsbWa$L)$I&vCy>OS0wWE9Fz|of*|H2pEtOY&ve2IS13} z=c@SO)vKIg%pyd_Q zAA{t^=s2ku1KOTjjm+&+v!OWmdqzz$8T8 zzeX&LYb>CUr*=%UG6El@Bn$oVRcoO#A+)LxeF#=dK4FoO*D}UIp&v*ZEu(NzS|hyb z(-&``sIH@Q%ttZmzoO^JA_%GBdJMjtp!X?d?&V2d#x@&ryxMo@dL~hhB4=)F45X}1 z`*tuSs1FBTtV^`QUr7pYCzwe@anIE&eIu!dsa{hX>o8}gK2 z)a#=v(qCXPm3{uq{({74EH^@%LBpy9-vGJ=otgwg0DP%O|JI~T0ERnaPm>XEo8uPoy=2pRb%_Wkr ztFS4b+4ZAOe6c*ngeQ4cy5%OecB4nsge&=}mDnN8;geUxR{Zq(8fwT_^LGimDT49@0m`nHEiDIk1Dh61=Sb8N zijM+Av}^We;bM%L&zm_4O)LEKXbNoy|`7dnK;*8 z%(SN*V)A1y{L0c+H4}OJsz}4L)gYtJgoB>xqem6rC9UznhjN{qL8Hm2LTI!_kxw8- zuyc(|Nu~WdD8%{n9ekP{a?TJ*fFRtP(*-8_o4)w_q^q6Y$F^i99B>L-&_w4X(luq= zutdBdL05q1WZcVHL3?CCo^a9OsIHS5a=jY31S~}8=+UyXtXK*1K88F0GAhnoQjzfV z68^+<1@CBpMgM{Fm=sq9C*^2QP@iSFR-nrhyK`q5U_KI&KyhYc1*dhAD4h=@Zlg)-xnStr=0ZQN01d(6vU!{P1qHGx zp2`)Cwt0}jUcaD@Nzj|4(eiLjFB>@~se^h-K8wnkY1VjrZ zwh>r&CAUKg$XIpPb{g|Fmw@sTk_F`}`q{Gi-a##HrIv%Gdg&1yoBr=)!w1i3g4oaK z6&aQG%ff?l5w4HYdHAGDw@VcE%gIEX=JvC#ZYQoFGh*R~JFFFvgtkmU?gDvtH*NE~ zOi^$>F-wZ;o0&-w#Yc~~RL-q#oY$H;_ea0`ek?dsm8U+psmGx#{Np7eASV+h9}`gd z+HIOEjp!kY7*BTcD*H8gk&*$7+aV=Mh<`(Ld_uq#daMLJpj%5d)DhQL#pqE5v_`|A z2u#-<1!yJpRULlMtLZ$Y+~N29u-7Oyd|h+oP$`0bsHDUtfTlifTI zdMQJ^I&O)uXyJlpZYZ4oNKK-giB9sRmTn&QK(j^zV9~nO`|4_M5 z4i{}cu%RtJ3d&Q`P>JjC z7^_=iL8%>@tf?%*mtF##&sMXdjxkP|8_68v0QNwT;xF^1C;(fkeSeq71Lh)BgMYPjB(OImM z5+q>>RaYjnkJxa5grM!vytCWm1*YOhX{zy3Lhvc~RsJm1p#;K8e& zi*0qKF-j}m*2l$ycqRh4P+lQ=Q0B5N^8MEl9`eBql7Ut9A+#TTpW}xKmw8Md4IbW6 z#43kStc3xU7-hAxIsFIY96*u4V!K};^2DahYD-%^jr0%wL?wKLgvkqQe&~G{FVYeo zM3f=VVU%7)xhpv!;kkn4DdccJJm3xy2GU@8kLE-JQoM?ef=|jv%zJH4z~#M-fs%kN zhUSy}mWdu(E!%z6EQL#Ew3WSamdCbPrifNxCM95Wbgat=`2ZFtl?h5&_}TSSuPouV zf?G*Ujhm%S80{7zc#X^mE}U7(uxVAesxYx*!N{jzLWaPXspD$K>$Qc*or$aLm)-2d zTU`R=CW!wt1t^~#PhK~&0l4vRHY%@mY;_#cc?MV45d%_#Pg~F#lTKYaWEqKm;t!~E# zJFfW#1(wAKuW9C2p*#PWz8s(FiG6v(2A83S%Zay_iqE)54ji}am9%RWNSK_dU7Yx+ z%e0BNl7`zWRg@+<@-O7h!s1dh+*fdY4F3FRV0J!0*+X6zKO5RyNA-=3e5c-fAVqsc z#Pkg|6uDkf(!E&;=;`!<&|wrQBKoaThFsV!EL9gU*z*5sjo&W6zKm5N@0Ie zl(jg(EYUA&&Gz_@X>8C1M(Gptw+YAw(p{n?F<$#?aUtJVz9F7(5_vbdtWt%apzo6d z$l?_xg;Szjy1&8`906&kC0&v?npI!E%Fj%xh9qLZQi3CSR@sgjW+y~bW?*IRij1I3 zs=LWq^`WTek-n=I0%1H!YD3(Kc(ua8vx6Aib#BxFBeZeDtWI)mV;mFNjYo8#^M z32H#K1sb5H%W=ml?3SFuv)zBa!P{9L^U~vEPU3_rDK2?2;N0Y2@h)dZB1!w6D~v2A zAJ6-+NEA7MKpIYTb;@frT(*(dRrvhyJ6v>lRbyP9Ps%3bdSNW%r?^o>;X#Aj5Rt6J z&`@7(Wsyw!*k>b*IMWyY2UF?^Sz01OTA2!TIc5LN{mUcvv4eG9ciwsBm13GTuZ+s$ zGSh|aR%baih~J|of(_G~L7zK}p>6wcQ=~-sRq|Jz6xIF0*(T;x3KvpZ zLk#NkIW?rNk{__FQ~4f?MtqerqotxkN4@Weg}Ea;pCdM$ccl&pkTKwAN|fHATD9t4 z4}3k!i!QBXf+$UC1ZwR=>?*zjTCMT~y^5_N?+0w5^2K~9q({Jz=lDk8Bx<236{Ye9 z{s$>XNl=X&72~DFCefLsi%pSjs{O^+J*>IAE6xc9n@{W3A%xo?qbx7 zahL?9)@r7pDJ>IMe|p+Kcxs5DR2)6mE&t#2PxO)!10 z&ur`Guhk|74MzNnZTTbY+jf##eQ~3Rby3pXKi!R&)LFw%8KquKw4Dwtiu5DPD-VXm z+)w7tBDQ|SF<^Kkb~O35Cty!GIpVpn=BR$sRw2brJ7n?r!;MeA)!2VP^X9A|9>;YI zq@6jM+Ud{)B*^0>x0%pZyZ-&!TT1~l{ZYI^!5-Hw(%xA1xQYN?v~v=LM<&cHiFl6S|0e;3j+ZXiT z;+kJzAAg^ARm^G;Z`t6BSg07uHx4O~7W?D2t^u9Yf7Dko14zvtL@ z+FryrKmG91qUhPJvb}CvzFF=}cRoYx;a=Lh?6JY=wL39b?M4AxkfY?;2|B)@2~LBL z8x29UOGbVVI}K~zkM_SiCjG&FlO$kWOst(&Hoi8y{S-bSj_@$b?}mrK4AWHfihRMy zLn6D+zR&4=AaoLrHa+XDmWT|QlKYY$>+c_p@;auq)-lt4kI!{^I7xc&G7mTcFIlH> zW}ISoWTXKXk;FXfbSvh!hOxICYbXlAIo`Wzo}r!u20G@Y@yS?acO>hau{Bb^HyjN- zhnNMnWoFW23VfPa$U7I9y;dUP1b3Sq34Z!G6(KqW-92^U4u3dP(r(9)FPHz=rmEzMh7 zpIEh1I#OP1&}gHLwcbT4HHVqvLYAH)Yp!GVPDN=368xQn!;G6H=MTP*$M}TT+v@B} zgr}+qv>;-a?N>NLZhx=SR*67htS*i89kM~ZjhxZW!0QP9iGc2pb0$5b_4ENBI6=w;-}5J9s7TMnv=sll@TgE{Fs= z1!Z*mK*E@1G$z$QVtE9~b7?&8b=G@oyEoI4Wj}*%mwYA(zHpUGJB!QD-M>~OGiC8t zfjoOG3S((WkSTmcT?{a8yPFw{LJhAJz(Y1-@@So2JngJkGHhLuW303yS{S>5lOgs0 zSv=!YErc{kZVD2Vej{Ji{y;$J^c*`F75lwU?ZWT&prE);4a^8!FD_x7$Xb9+FIK)^ z*_}fcpj@AZ>V~#CB7x@RBc)hKB{0ta#j|WD|A%CE6Stt&ysIf*WEah-<{RaO5`tqdfR2YL(Xx^f? ze|k`XO}7XNp@;`;nrI2~w;%aKl<0HX;uGiE%W}%fqV+V+x7X#2bumY~bwUuqn%XKI zs7dt4jP{dlzM#0U`_ZDIp(m@$8+3o$W?&@(%ZpAN0sqzgJfNkmj~C`+-b_ zjs0$ZF;p>MuQ;IEk*3uwh__@XVOd^-wX&(lgK6ahKn&8DzuwZpUE^h!(9Lr=L$fjU!Jr zz*hdrP&~EjpxE+Z<01^I-#7wVLCx4k3=h@Z-}QM z`M=Jq9^IBIEhQ$rvkZdwuB!$*H%_3q2D`sV5$9x7gBvyHF#kn#_*OoYZ0`$n=DM^m z(gRQBokWnH26=LrDk6TWD#u~4SYbr2skl?G(@-cc;&Hu=mxaGweEX&Q3WK2GSJ5YQ zq|YT2cByg*1b}!T%c+&vV?0r9QXXNhd=ZF)2o@uXRa>De1iE85?^YdwN$D(mWt`V= z8k3lWU}}Z~zG)WtS6P(s5cHbgp1oIj=>8-~$u7){BNdY?nrUY6Q^$e#O6%5FO*@RP zb#R6Q6Yt0tH(99_L;X01d`x`bXl{jQJ2J7eQ=F@s0OKp2qFh^L*ip-`A%c%Ny6)|q)jJP+wmw4{kVH}-yKm=Y@#hH<8{OQR|i#iL7hE|jCD6RIO zzntIseQ54?|M%)yW?o>}UoO%w9ucnOFju;o#Ej7=?Z>55H&slHCD2)7ixi&}FJxY4L$Fx`b*Yy^t;o?4q#D z*6opG8tJ6Rnv?C%FY4p(^(JRe$Dn1qC9`Cu@=6L6Fa&I_n*95C;y_^osF*3h{IvT)sB;;q zxg{Qy>oQ|6CJWG6x|_p_?>{!&S*~&r z(qdV~kSPhGCC*ua$|Q!)f$KygX})FoT{Ez|^pW-N&_z*ZN)0w%WY^!<0~^rA6xka+W!sUmE-*v&rx7^LvGdWw zAo-s!m5kD8&L!SHSpr_%A{p%qcM~RWdK-NAFge4z8~c@eNlKErsWt>+C$WNNx*kt_ zGW_C}>?_gN%5}PT(qppl4c04S(V0kk ztw+H2(SwbkvD&%M_9t7v9Xi>;7ZaSfKhL-MBTAWSbqG6ue*PO-!G0gOhPTm#A+H^o zcdp#rubB{49+(s8p+XG8_2|}R15u=)(g%|e!L(W%5q(yB=zW(^EG_^pk|uOVZv+{r z>aPdDs>J1nb0UA?u!0R+kMWivXe@yiRfd+8#dW1Kk4~;5$+Iyi8~XeNLTinYYV|gV zCvj_Na{oe;yZ+};NHn+`EabB@@R8rSJMTw8mme0&)7a{ z@Y@J(XHYgJs4pf47G)d;VXjlD%saA0(b=v8E>kQdlHPTZaSsVm>Ih4lP#dapjtoQ2 zxd2VNXetUHhjC!THs{pWU|VxV8?VCJW+R=`ekj4SteJ)#lz>52hxe>JhP7(71mM{K zeP{wJ7O1J1d6Wb@`SH!Lk_3JTA*7YFz^m;7~Kg9IO+VF-;6rKG^pdQ zkyVLVoZ~D82P6oM8q81v1MmP0ajz!{3jx2WB%#r(As#$?sUjer6_&mKm1O2Hoswf| zMvBB?O?AffI=9Ld5J{E5Vv;{uL?~&jpRS=Ca19JND%ew80`mTX(>ZCNl!cxvPze3) zf2Ln zlZqy+#Lchr(0Ha2MV6;hk1HTkAFxc9eaEm={*_;=5+qt4h6gr}=bLGDJ2*-JzSY3L z-x44$5B4=`IehO=5o#ebi_#Ez^l(ulaRH0#ylnnNrnx@ODedQeTOe5boHUOCk*82P ziiAf4uTGUq_+HhoCwZa>Mgrur%JsZo&9fsl>c|-ZD>wrYA#J>Qv={!{OF#QB!w{47vFL<c4nAeXjxgXGkp&_s>L^rcO}XEunDLN&?*6HS5xHGHwm%Vh1TP5M;8m@eP{4y z-up?d=SmAVqUl1*x;Xp^+5eMC{SU{Rn@Fi5JL8M>Ddbo-ft5mtOmZDLS#45^7|*LQ zv6s>+8p}e`dwU*sQd9us8|jlE#EZBL7#EZ(T`yK^NxEy4gsQ$3H};$9*-9W3t>bTS zx`h7|E-NSyQ^$QpPIx>j;y%~K1W5@BtNG`k`MQ`jj?x%TH!{d8l2 zn7X9Sxe*V!(d4UBu@GqI{|9+1N-3SI%lX4v(mLTu0zd5zxf$)8bW!(;EM5++m zI&OYHf{T1d+%W>4I+2?|p|F`j!^{`UhC zHJ2hou$)N%67YyOMb_Wd3BI3B4V);Fw2KE~3hC)3s5rv-uVkhvtFh~-ucb+Z4C|^p z^XM(3`IquZ!KHny*HX{gB-1z=|G!5BECK8+NcdxV)ssZpOLKNc9s1v7=X@SO2hBrpfj zMwO+MCTM?U8rV=YaZWxX`RppuU~O`k1SKWV3h%cQ-yWE!6pGAK_hK!bTF{R1T0_+) z7a}p?`_eUXv*7t@=pRzSzm`?;o7W`n7b_3dY$%T}9{#rPYN>ZU>OShRU3bQbLQ9Cc z_$AUH$Mc`V7jBY%v+CpS7U`xet!_qC>`%$371&fLWF!&i>DQb}ZJp~JtxLHkohy$b z0t~^bd+35CE;y?AGhIeVptmveFzfil-v~)U7mcHgx$EJ|G?33=SS_j$K9gB~c=ur0 zrY>QmcsYHWbX0EanqhkNzZa-VIlSm%y9iH(4x}xTxjvy7t?{ro7KjcN6(UOr<-~9Z z)CNMN(dI76mDQJLkS1QCio5M6m7wL*e~4FCs}MD1q_MA4wf;&LPS(&}WuHzf<{`o{Pf^%<>%GPxsvAf# zk!L?)AvQ@`#zOy@E_}7b$sjtsTbxp&vj;&x^B3uu52dmHV|WTT(-`tQm1FtTwMqGZ zzn}j(iBy7Kv0LM&&)Q9Ujqj$%(o1$Sbve$$VwHS!CHu&N1oQpwe5ydCv z1&(xUiztG%yE3iJ#TC+V7B`chnzK8+u=E`eiqU6{Q3YP+WTq4#iF3X%=-hb7{Gf_B z<(OWBDVr1@9?N)XydI~!{2uX-)Rwd^5RaN;a>|iuHS8fQSSQ_Q<>70B&pVG%hd2DV+ zmTyg=UG9J}OajJ|LWizgn3(PQAaJ^;#O_OebtgIDiY!R^x>N$jK=*}^LadJ=-7did zK7C&QUE{G<)&Q-mfoT|BpnLo84tD;wCA|(#{z=LXfRiMn6+t9U=czJ>tg}djYDM4J!xcA_XHAkGMOG(xl&ApsEr!8>?vRLe)2BZw z&(98AuLGwW(Cp`mQeCI9rt^gpSQL2PzJzJ0%Z0o!10Eb^Z+2`_yaDb%o;dfzaZuj4o*S8x@gsr zqEyi+aS$HGJk+2Wz=hV%0U$uS5BCYg%fn+`cGFEv5L0?=9)jd2lDFIR5)`l~`kDXx zB+9+PSa0I#YEd6~p;2BrWK>RA|4b56%+G}HBbB>#*ib|OBh6xQayh~ zBJBi`T{q!iVs658z$;N(lWQO+nP7rGC;=$Yp(&|wVZMr0 zBp!01qkMin`#ya+(eRowMM!OQF;{uHnh267-}j}97CtV@34`hzKG*+cLw$rl)6ZD@ z3HlI_RAPUfI+=qvm$SBfr7YB>Zb1&BD&>O*^k$aX2tP6Lly5%0qrF(Ibcwu zGUBXM34u~iyil3UEwI-H7e=L29*(L;jrhST9{1r&-Di041?rnSi|?3-MQ8&PWQ6aq z)?DI_)yD?QV%1dwKPSHDzYflCiRkN1?s;qLoHIfX9e|kt29$5%2FxGMC4*G{A?&@n zR;CDBMO9RA>S}p2z&URCDp+s=29cfu>T^4VNu*9*oG@~FkL(BNjF}?Ce&lyFkV5W@ zHFRjWa39CNwJZOr68|iwg3>9cOzy)S88^9_8UL^+VuFb2s|H=sQA3Fq#$2iQN_xTL zU+jP3AVa|v08(2G8^g;MtH6$CKG^@UCnAU7Xgvk|El2u~82`^R!KwjX0~WRF(+lgw zD-u)hGqiyxB$snS25s3!>=HQ%HF8QQaYemjiZ7vcu##EJuiqnM`c3z+UPGZ8%HW5# z+fB1{?W?mU#`%I2%GedRrR*YlLfo+a2Za7_7Ci%=4Wet(=~X%3%UtQ!OcTa8-DEzv z3?RZbTeMconi(XkUnx~Y;585{a11Rf0hS8YX%% z6p%V*CGndgX_0@A3`fbWKZFL6@shJz&auOzVEc_mTs4bH^31ry$_bb!qTF}m;{D6F zsPFTYIVr6bOh(E7I#$>UC~FSo7{dIMZwz8Pc4^eYi@B^zt8`M(daf4GuQ%>`Ssagn?W}@28~EuBXtl z{hWqU;ARFm8p&LGeUPy-C#z9iE-ri>R_2+s?@|v^UVY_mU4lXk;{#G&o?7^hn4p65 z#;==P3SjcNU%LKz1^eq`Q={W^e28#*H3Eb)Sh#%W)0N~^lC@3!CDl<<~ z*OEatDQY3{E|kR&=4kE#jpifMJ-6@qFVD-$n^|R%`LS5Pt8>~v_|_lM)ys|-2bMWY z09UC+Sp3i4&7Ylz&#%a_fH;_NnO4!xWK0Y$k(axISklS%%9o;*ANucucL8R-UTn|U zPFd(NSTU2g=VMl5%k#s^>CGXb!!pjoFRPMl7geI4%^j;ES{AQxI?!6@6O=_HVT7o0143C7;(qbXu137f#uwv|JMT{#F94K*T()E zgY{PZq;S&^38|*qB+g&8@LxX&;GimU(}2W2_8q*s9^~WXVwbwTGbDJxg%=S6a z)o7|B_-fM@YQJi#-gcWebTqhf3I@2Gv704xOMTyIsm*zx1tY$NH9trQN~c*fx9yMd zoBerC?KLvapjTlX>3Kh{U~Ko+s|7PV=c zYBJoBww|v-J!MrgHmR{ztOswOoHGCRTDaTh+yPn`vx4nlrOAQHMyJInrZgxw?-;NPifg^fbMPAjX9+AXti{zzzZW7lWSwbE zsq7q;bAKF$JC5@qZD`ao9>A3|oAbSWzP;%#D6kvYSy|$cXVzWotE}x3`jI^V*2}gw z?@RFeVf!(pm8vpo~frXr`G!H)AqnBq?ZNq~BdSM6SMEl1z2M^9tG zEl?d`&x-pm_AHd2->KSE%O0t!H1?64gv`fli(C*w={X*%r{{baCxa;eFaElLw=|4a zG=`N@N(y5y0hP|^sU#{?+(&+icx;wbJ~wWYzhxzSXLXE8^gPZr*Y1>m^brphZ@hi1 zlNg~FP@??s?Z&zUVqa{#{qU*A7}HA-c1FGN4V*m32phGUZ5C{It~YF2xE+!5I34VF z?(io9%~JH}1e>Nn+aik1?Ft^FvM#OtH*42H{nH0pf#tV_B!o26ESjFlc>;=9B+*Rp zNRj!1L@F?ybKhi8NF8AT{c4aid%Sh0*u9zDHPCS1@AA!?*WCqM=(!x2Z5ap%?1_+= zkklep6**v!Feu5LAp6ik_PXs=RzU-=yBbOlggm5wlM-C@sy0HepyxZK-Yc`bpna9b z!|7h{>s+Jjt%b|q)(aj77648S;#_VwI3Xdxh(nANoGDkmkR&s&mGv~W{!{p$PF zoC4brT8DXk&e(Kb4pP5S8s~$VPMcl8#A3mtLWMn4?(28*&iiacpv&?T*$&8yo>=D0 ztHtpuDCwYbQUnK&0a0V2w!`g9P}(`0$SUugq_pg&-~n4&v_0IeYlqqvRt-&Lxbr!Q~O5_N$7Cy{`S18rTMZ_ zTi;{hX5U48Jp20I;iCSc%^x4Ola~qtI7nQ+_uH?(?$t>I3qmy|gXfq40(S@!rRk{f z?Z*)nKJKd++Gtg%4eY?VXD4qCm~5W&4SsIZ6)SOny=8j~3!?$>P8>r3WGw8MmNW`8Kh1rg;J``J`p+1P z{ZVV@Q`U>y^k+x)06=vw${(*hY(#8y6E6?>Y~Hl?d1k!B@?Qvep8KMQBT3;hd=CLw zjOv+se)?AHyeL-41V;d@r8bvizcdH8Q;5>k+)kid`#-Z)D*YqhK-1k;a<5WqFWH+r z4kzC17q=(Ed}Y%tB888uUvWNoTGpZ`JqV}9b1kPI&$N1-qe?Xfrp%$-R%7D78^&vL zXllOcW}uk2)`FRae`DFgSW<$d`2 zGDQ4sb zc1SblB=9o#}z2R*|;|~nC@|p<^iB+oj;XV0fY(m zg2n4Bvhy{1JtOy{KlA5H6t}K0F7_Na$`@P~7^yPk%Gdw!=b#(d=_5MPd#Z-{^f0qL ze>ok_@ZNYH-d*lT%Tm{trq%Y>Kj($B_S28 z%$0yVj@BE&LWw!VIa%IQe$V=o+|U{8^HAqu?Re15Nn%JPvLgw22q}% zXY0eZKki7(68t^*YEu?}Sk!t6^q1lX^bM4x%)yr4L8u0KYWQ2zO~9L$M%L0Nrj<^* zl?8W{4PtjmzkXfM70>)iT0jU+PIsL2)W&u+(IW2gVOjc!I7^V_vpRWhJ&O^hnaw>P zP{$#OmyM@KjFO7rn_&W}i~PMoJt;8(Z3FlaVwkK^Qn zQZ@4}e~p9SsF4s32a?*(3XerNz(d<4F!b<9kt1&N;Wy9uJMZ!FFliyrM{iRwi3cVm zs56x=EF`H@KFRvTZsny?@ z0wi*aUWX#aPD5k{-p_Vx=PUh7NHMdF0O0;`-Iujh7R~I=!uX8NiJ{lVC$%~?OF#WQ z+wl~6Dz2?Q?D#j`F5W`K=YrY%f&?9b0Ty~_%mgsICQK<8k441v)L0_jXGL01w0QOU zWUy=xnArS2J3(7V{%E}gw;lRTzVR!D)C`a`kR&AB1GZ1;ZbPPxOtQOn{;zfiSfRKQ z!JK=TQt|c6syVMn;rHb*LF-bRVCwQnr`_sY@bkV@bA4CnqW9%p>cNC!P0efL)r7QOug^>H8w*SJJ<~`qI zDV&Kf^-RtW9z^@&<3!+;F*2xYKhuRHjr;05?wR!meveb^RpwS_1uQkpr0h47&=ZrA z?&P_AeCAC|a`(H##Quxlc|_p-@`hhOil1xYy-w$ENs4PUIPf~dj|F8jvK{1oSnAO= zs8>g4{~DIUAdOT=dxTGpvi4-OJ;k@;+XE(aRSiLsF(Dgt#-9D1o1uv@i+j)RHNn}G zYz{Vx+Cx;hyHU;y`m>Em8R{E{Tz0z>Vah43P5?y)DZFkZv+PVU^&HCz(sVnMx->Zr?M@T*XO~{<-X$AU39nbg-5qb=dy+iX&_I=t&Rz3qsk*Ecc7b{v z`nf{GwvOBmhEm@+j11Xkd~)^{;#LVBo=D;$V2C}fRVk&^3#{yjnG7Xz@NRu3Fn0UK zX$kg47@>&{uk5u2HT?5TqFm?+9JAAiwW~YTu2CIt#;tgwV_x(rb9cc)_(R2+J zC7UDn)*rL+*th05_ssoaxsB{APD6l+`j1}&&mXm5R1Jq}@;D5=mTl( zh*t1!ZPD#rje5VZgF(QQR#j*sxgTGY!>3GhWoMZ|CA_oc#*sqfGG-Uem1&G10?^=)1$U7E9~*%?h6S zE%M_-$fu(j%<7fsVkFBkC4-xfwfDtAw5eVw-l@C}cf8v3vnZlza6tA3x3ZLVL+{pS zarTo}?zz_?xbb!bHnDRpOTBvq@P;rpzWC?X{`U^Y@gqlZf*i;ZTIwd7AGqdW(+c*4z#V-5OaOm=q=Z)Yu7LYPI89Ty~ zGk56&&U4Xz($wrqJTJY&wc1v#g+z(g4Y-+@&ydC%n!k}=$B|wlW0fT8*KV>n+=pc^ zwN}?Jg{xwPURYeUS~bqUUpH7|%6JcaO0@pQ(e~(tR=;XSO(rflw>P25v05F+x0tvY z{)@k*@yGdk&7!Ra`P8nr_|wXv3B_B(THHS*IH~ZX#rr$4nkDatg#-f|sEOjhMds`K zoEKP9qq5ZJRU53Ju`04eZjepWJ-L{wYh0TB{LXcA*%iY3Xqh;6Uydx@C8CT$XZGge zJp8A0Ks@W&3c$^#081q2w*js7M%Gp<6-|KEbH2!QYvoY;Ao{gYykciNO0k2RD!;v> z^=>xs;nu+xo2b2QN*T%aJzBc~#PL2|DtkZJ+}+?#B)qzbjPf3;ah9ujqYC;3+whod7SkP00vq{i$v=@LW8wWfw}vVu^2dT%NoDP93Xv|Tk%%14`j z4|>}hnWgb+)lhriK;2ssQiMRQ8)lGgy2sEejux(I>}v(Qfr@4BzGf;4Tg4IeC3YxZ zl?EV%lX1nGsH8)f{fyZ%)_taZG<^k0fBg-!>--gK?Apu)Go9Clx~eTd6f1qyJKZD| zm)m~G{i&hQ)Z@FY;q+P=K`!aSh>o0 zdUb@$=8bV#ApJyZD`#ImY2|b41syH|Cz-`7XNmlB zcbnG>QrR8I3-|0E0eG*Cd!M`bHP7FkOP!=TEC%w;VP&i3=v^K-18@a$*e?Oe%}TaX z1&k-P#UHTU-}Tbpu_%3-)Dc?7*m2Akd$o&r%yFTqP_J?Wq}{L5j62sFF>r5Y$ZuMf zV^u^tGa{Vc!SUH5sJ!+yb`5F{XDwh}@_VtYdrV{_r}e_Et~mdvm>SZeTlU3la*tw#iQW;Sg&fc0q^)TNhH<%{K7J;)SL z%w7-B_EUJv441Z8F^hOpRQB}ROCBEI*B!_}GQ>7Qxm}$`~03aH14lu)yPzw8%kHQekRm4YyUteGaC+gHL#^ zf*(ML-}ZqlcdU$}EoSod>shBIm%FxeqkQIA8@+7yx`l7vIH_un^8Jo-~Q~ho!L7)(^#X9oP_9sZidjYlPU&l#~r1 zR~T*yv{ZFW!)qq9d!Y9k3lan0o>~4&&VuaQHY6>Mii;m+vC!b8%NxsRG1d4s3tcS_ z5|W(R>0Kdo=rhOO1n;SC@R|&t(XK62O5wLnfE##=)FQX)u5pz%^@oNp8dxVM?OM1P zY}lU>ts$c4arXu%ljmV+$@BTgu_bqu%G)Mp#T>V^%~fw$PBgknU+9V*x;b)aR!z&> z9WmrS)_-c7a%I?A<_wB4DzIr&J&fH((QY{(HW50kKfX6947zAlPnpm5mKl-e*uwdM z<}WS?cDVdtu%a$@eq9rxlvZ6}tGC2E6ZzxE#Id!zO7(luIDIOI-EMpJU7|FChKD;W z=Z%Z{DpxA8{0fn~9aN!0k6%RfoK;;05yN&#$O`QEPq*!V4td^7@c+7z%;{!1EVoC$__~nfNo_*|+R;7aXSCcxo<(z8DW<`z+&oRmg5S&5mz3q~ ztXS&w7riVR?6baKv0qGp9+X|Zaa34^B_+I~^th(=4gEu#r zwWFQK;UW9M{sApBEU-YC|9bv;w07F?Q1PkTCLYxTbL)q;M^DIK6M;{*-Kt`@I?&nu znwZuqoirz6>E}@Xr+AKEN z*+=pwkVP=N5DnafL*%}XzB==r0^(&& zy<)IT1x5=Ce=nzXOuk%96))DnpDdeU269$#6uxQ0&L35%xL?ZiKFR&X#l#mRzEaEmNELdiC>2K zX|C923e*bUpug%~$r$uhNIKyHR!OdcR zFY)}_6@K00X0FxOg`V#fbQ*rbJYtehrjs#%jjeOOe#m11rKb3RpK?M4{(jb4m1XJ}1?A5E{kbG&}UZ_-15bRqsL zi}rySnS=rN)MxWnmo|3eQI}WUCY(Gg8>(I>T`*qH#99Mm#pwvI#ZcO^>r)RLsZ&B?3SN7fqDCnF4>S^f`>MR=O;Q; zk30sB9-rVYAyNCjVlnO3z7>8@HQbUkY&C`uV!%Gvimen`|IzzzX^0T+=n_}V zYXWTvJx84f)NLj zSXN9P&-@7IDDIP#)m~?2Sq6sxy#fD^X<(x^2;Zd+>mSGEAu$rbh_Y`dpC`NMy2vM z@;bHe(W%j97&&;8j|79F3Qs2%t+y!iRyRv}VtU?RC9j5< zJ75r?!~N^^?a!;Wz1wy%R2*ONG^w9P-W)BD7R7}ho%+-v$3M%c!#O#6JQw0I))8CW z43g>?i)cc8e*8cqRKuu*kd&r01x<}q6}oNrw)jKd^@U+NLM_pF+kzx6ubkOs!ATgxCU?`m3C363c`+@|)|p zxzwasXTu&iQky2cDs?0xRIJ92_|h39maS?{1G z`C6o)5QBuxGJgiOq4)~pP+Jp$VT-)D$J;_E6;cO|M}9_!WS0yELX(1-TVrTXU= zZabrIa{>M9s>K_)hjx3aX{@D|KpMD*zibN`=gZ%F)IBIH%x#G~x#aWWRvgTp&R%Oh`m&$>M(dAXLoQELyiHwpqGki%#DblV+HlKhP$gukD4mWl za-J-DCg+%IxBgU5;*0AR>C*?xS2=ZMy%L1i75<6(ZwQXFXmYlvSWYT4Q3j0jBz|W5 zZxdWcm4r|Yd?d$oCVwIOYz0M4#yfva?U~re$L~@FDrDcJE3SD}l?6%kO2l0W$m zFXcw^Mbn$EV#L;(8A7V{fpvqGr*P~#%n+`d-!}wh`_d#jN%PdS#}MGp9TGIEe>6CS z>a5BQj&SI7Tqj?Y@xO;q;XMLS(V!~Ror>6y#wqgli5 z-gg=~P4p8&pr6-irR50swvpB`AvjWQqnkouc$u+un&sNh)vhPtPj{MFmjF+~JiqG5 z>!UacyHZG|N>3<3eXkDTP;z|vm-FH?spQX(Sjjp#n@^Q#jw0z)D`GY=@H^aJ)}e0R z4|J;7Wlk18bH%>G0`;r+O6$M%a77Cclsd1>)o;<{P^MR0SIzsd%3>kw(cSYaKc7gN zjV%s}$Ep&K9L0+NNHV=WYJyhKe@trw)T^ZwB5bB-+k%G?njP z#>-FVg*Jt3xVqYrOLSA?y;@sMORefDMo0<{Nr86SlM|z1NQBpY zGu)i;oiNWA=q+_G6g9M;QVV(ZiPo^V_{sRwk?6!uON;(xGyla4PcYk<+vqJ0RCH1w zu^^Be-rl!sx*4Gzh%-E9@e^Z!@$K^KMW%DTu3qk{DxmJl(?2E&cJHP$&>+sSFbz~w z#uTSxxo;0pB>apn+O#@8_|(~~*c!d{GHou7V`8+(Bf z&L4G}$rOpmXBglbnUs&BLA@Isilv_cLBZl=VFke7GU=WQn&VG%0o0<#}bc4Oso?B@i7A6r#DBw*3rSwCA2U_9#_b zZPfW&JDoB`<-)))uSX7IX!&e*iXV8)GRdENR-@~?oiA?}@_BH8v`u_WooSl!Nsp%6 zs&rbM2wxN%on3i1{(S{)E*{18CFq-w$gw5G-!IQo-*0JL8$)8mC)7t2b`(c6Q`0Ya z^5xetdofM>o&+$zB*YQ>_%$$XK174?^_r%9xpB=qK2gEGXY*s}Y(fOrX}_+%*p+8L z{hLpi`hv=&j&7qP&h7J0h+`rDPE#dWNx(t35X=U&g zfmu{DK*$`$jM?5tB+Pc@m+Je$FdpYzhhlzA%N^9OC@a)W(4YN}z62)L-m&6nXhx{@1U=4(s|pCmq}FB@voiQ| zy+8&1jjy)7^c4qk;RqWRglr9mA7&){d%3TPuf~d>xJ1q<71DQopj7EtEm;YaD&I4E zCFFl`D(v*(yLV;%XyM=T|MyPm&#PThvTeL_T|zIU#j37@lUIulrPZV9{~UX$V_z$_ zp1ha}I>c+R%^v^yS;h z34UtHY1%2~8obQD%(5)n@BU!qj_IuHGadNYk`PA}|m#w2|*O*i^CQ=^=Sh zauO;uXaVmeWRr-O*5lPO!eyz1KAn_7^y4oBm0K+GKJfOGxk{0>{C{JO#6%lP2+<2R zz$`Vff$hD~naE+4M8`$S!Tf3={n#DyqJTb%B?QDWsO1z#s5Vbu4d`W=C{nh}tukbE z#g3A%nNy8#4FCOSu>Xl?9D4WduSv9Nl~K}B2rJ^)G*4@ZW6-yt;>f6e0fN`6c5=jo ztlNF&LUKa$QLBkf^I4K6fC7#Rs4uLXg|~$t>u-%y2ldex%#EJ^a$CxVE)#X zLLA%Eg-4`w`Y>oDQnx=F5VFDvfHNo54#+_>s;9qB^=Qfy`#UBBSMxk@m*aVD8pQ?;>+N^~UOMq|G~)ld9+O`O zriGBnJ%>kwgh43m{Y}w_bxHRSQb+94Y3|a2SpJiONBOV#PJ%Ov zDV;FyqhkJS&+`v|_aXU%m>R`7hM21a1K)g(yfwRh+8o+A$Cs?61LT}!qG*wk@}PWE znS=V3-O5s7^WqJvD!m1 z7~fNx_CbOgKr)SJcIV2V*amU$A$@IPPeaT8x=pJ@aPq{FHFh;DB6buGy~%lcvR1gR ze!LOp%-CJ_0WUD)Zz2f|0dRDh;Nb-k5!lk``@Rq5ripCPx(;Stoy53_+0P>jd)&ht ziuvUE+pK)tB10OBF=&CsH!ts zujS)8NU5p+c7^`iGwK=uo&prtfTVUY4HAR=uq9Qu!npQn20EqkVJ`9Y9i;R1g1jN= zbj6`)L3p7?q)5QGui3@iHo_^0{=`)jPEkh2?#MhLtkS=;s|Mh5i2Fe)8KOaK=Hwkfjt;I>IH?tZyMrTfOXWKSQ4=?OW(K8tlt zxcTY%`gl=^5nHb4e^y!kksrfFk-M)jWEi~xO>)PH*bWsM-4_7Ul<1?(l7aOKNsUPS zRY?Lv**6}U*_7H^husqaLA;@YnijI+46rZPgE4G#%aSw}c5vZ8R8uEOjFE-kIJ{Vn z7o7e+*VII19Z4Owe}t3!`wHWx7p*n3rNhmxU=O+)!zlSp@10?AK3QlCqbAKKBs5>n z_8|8kxia1b`jj0(k428hi%tjFBD6Pl$NTx6t-`2Rf2wyj_VB6W6!aB>m@&*R+Y`^^-{d|?srQ~Yr%eeO>5zKcmI*`%nr zsj>zH&5EJCTmdw;H08Bhy}k5YR{~jgvi=hA-_S+95%_TG19qbe#Q%!wn?bYjHu(E# zm@wIMKLLzmoKgif66Qx+U&ZEEC6C8xyO`O}S7rMK*G zW0rzJ;p|STfauivu`ll1bnia&wE1XeQM!ydQ;ec3&(rD0{@{tu>h6{H2$@%dfKqpf z;;ap1flm(xsv5qi&Gtda`%s9uSC=%;B>TX$1u#*~c#w^IBWk!uL}%0Q4o@LhMq$t~ z!_H#wJ9lT;@WV0IIV;O=k3*l&A|5S9WNAJJ-|-BlmV&9hO&o-Dc-W$OTo$~_D81*} zua8B?bBl(B4x47YvvE%vEc?YxgwOi2ou_$cZ+9X2PP-=`!T~rCo=~N6WlHw>^9!hX z227sWu`)YSm~QFfTTy9+EcGGR^n=LZrJF;8d~#mKU0IsX3M1q+0;ZQ;b9Lx!b$#f- z@nLInv|5@}XAaN?w(j)4?zU+;7KGUWG@3mghb}+8fCDsAj;Av~ z2<)nLmFp}^7KU)ae59O-l{h@PrvzkzxG{nQ)#Gro9XVbL&`~%h&K|ESygV9@g5B|n zkx{UIi-z{hj%_wG%$Qoa{FAngg~O=K<%7=k-j@7~`51$c>%N+eujS!mqPRqZ9aE2k z7QL3&t;x*UII9Hh3yyD<8nK zp9OA#GC5x)$nJt?v{gLf%Pmv=)-Rm}r!5 zp}dobv>=J}%vL;ptS@zD$|Ck)=&X^51=)OyYZaKYb9XK!1mD4Wz?AP)yVnPaHq_k;T^pf72(hv+||D2{GT45iUNMk_&p)GEiw{iv;L1AcHX=#qf z%mgpUP#0bN%#@aNyPQb_ZZD={RTN^cGJpoX2vb*|3wbO6%JRvOsno@5 zVq>(0*iRV>0h(c?RUUVEkStq_7|)AqB#%m%I^Wq=rlltaWqAfKI2m@+t;N*C>gUTB zDSS~k%zY^gf&QZ0TLae1%%*YuiQjDxD?ldFfMCWD>@aA(ZYKqsZ z+Pkh(ahgK*5%)urLfRc=lED{$sBv>=YYjVtF8*NF+;>ZfP{YN;{-w$QX9WnbDpLV2 zte!507_R4NsgF$APgcn_dLo2dmZ{4nUO4ne2>Xc~_o~|wW@KKS$g=vJMB1{$3yHs5 zKLHU(;pJz;i;>ls^*oJax26*WYx0y6%}@8oXz4sFMK>ae4L;&T5r3MQ3A?^pm$V}E z)h|20$h|N|Xs_3(X)uw!tT2alpU-}bchougUPVEw<7x==ZUpBwR4$lh&D`zeO-gYV zMS9Y3RuRs%)y8r@^1crwJN>lsI|<;kq$lKwBD=dQPS)1<8m>+Dw0#T#TVF0de4eMv zMK1$1g7>RoR4uyedV2Q#jC(7xR8?O10$l>esBS&AjpA1{^fKSePuZJZDDA~|JP2Qn z(T5g)_MrU&%qVeaN<@k7=m7$Kd)UkY$=3m1S(IKwUu1+$e%F10jNW5hLo#|jVS`+J z+c4Yx{D`!@^xK2lOvzxzln$}Ord$2_5r>@(?RnwTAuwBUIY87r_y~hKp{M)enaSZD zp29lu^(Y2TdGm!xo9>WkBY48l*aL`S`(yMGzgYXzTo#Od1h8V_0Wn-5G4DQnfSY`* z4SvfzQ+rx%G2Sur!OMx+CgGh_S488Up+Ck0fTe1c39sCD{@v~U;AJvnD(Z8B)eDOb zrcUm;8sz9y)RphNX76pJ zzbHttsUP07o$T?xJ+IX;Vmdf(@2CmeBVGZaxr9_W-VoxSD4M$DiUomxCQF%lmsS zjpV>|ZR?StU-9~$ubKC!dR!zy4i`P?~J?Jbq- z*g^g2g~q{Fk+W9moNo7RZhUXU%{RrA(?yju$&TQ%WN=t{awu7x?i@cqpL$l@9vvkQ(ZKG7*QKv%ueM#YCAEtm|}&^eKW;{%6g`$eqp)j z;X|(D(!)Z0FF(cBAEeW$=)>=HCq#`EJoS`=2eWX&i;*NF7eqN6!3%5oK((58`pg`9 z*)p*mDod@*1mZX4CCkXv1J~EDam`1F+%v$eM;G?@xRu#9nH>M;umr9gag@j;Vi%og zcI|`~z8KHH3Lqa71Pc3ux}TZUKX!~;At5-%&!PKpq$0wC?9%>?+_6jh<0umaP`AkG zit=&%)4&N57l`3@isA|g9Y98Fo*%<9GJ(0jURnWpqJM1#^`c>dk9@;d-v>8)lJ#7& zreIDce5&!SOx@rRb7(M5{NlVT8wpXPkd{-WF^3hC)eX?!jOG*$R6)*b^5@tYdyU_Y0?o8;V=F$i>E4l{ESoce2<;rGl zFP1iTzaND{Z+G*2FB6|>F1}2uJTh1bLb6qAZVviAcA+nF8>6(3g4&# z#v=g9KDVu;UVYbsJdwMhs-2xz6yQ4V`Iu;MXTl36y~BnF8q3N)DQ`3SwuCfpfQW#( zsqy+Ergy)Zdlb@}zHM~5gtU^_!28 zX%nO~K(#Egm0EJw92d5*P>Z|Yx+EHpiP}dYml&^=TB*r`R(R$A@W}=GMrG{h0UY*Y zeuzJ#tj&$a7W>iQ44cW_`AkhwS+i)`Qf04Am@BqcVL)F4ok%en`P3~j+^&i=h)x0wwUX>(t9E|znRpat4PRZvk zO5b)uio7o7xmCu-&=ETF-F!S+?RIwVnko(*qHNiz#E0wagQ{ZLo6#7DqeL1+%u3a} zr7az!lhBu9r278!5u!7Kg0BStolUa^slRjtE2X|pcd!q6)^|}dm`XoOM;iGmh;<$} zjV>a{R2%hj+fq`m#(4ER`>rA)xF~j6(mAKPJJ>N@DeImk;!IloyVa97F~-OM*d$LP z|8#2nwG_Cbos^(llA}G+Q%zQ1RDQ#_VRbjG_O^g%Vmvg)0MUCQ>uh7u$g$MM4(Dwj zxhaD*CV2_C#yHhkrz$z}P&uc3TG;I1_cU7WiZIEce%l01Gv24HZMK+1ch=jI}Xv2 z@yXh(IFy1z0FW{#rnCWBkN-#8NAHd4nM&!wv#^`jy=gxN0{4HEe-Ss6qfw2(d!PDdw~k+=&9`8`>nK+bFH#6iJ0HYuV@&uU z?DK^$N^4;mg>Lh#YD9kPR`{aNKfaGhUo3>V*=)|?#c|gDJfr6ebF|c!ZHpU`gg@@3f@XjYgf=7h_G(5=OY4grPX!}^MKSw8L|}>074^C~?O#els$>Lx zo(3-!f$F38w2wRVDEOV^ZjFg^1S=kHI{2dXOV}Z?Gdo@%vZ}rpOv>o7x{Ga zTbx94Dz1sVV~Zys>HBt)QQ6oyA7ScD_lv)-Rj}6|;*mzhs zrB%Nkdl#*!Pi{Ng%s(#rriYpNbbT-5dP;kzZfPWC9mKDny<<6g!yjaz{3HN5>qX|zK_yyB29>D04nl^Uz9QV8_+njq-dk9uZ+2dgPOp$rZv8H9vOi#vcNpb$ zfg1#e!_?E5_Mgw`O%LGWJ#3JD-$-kU5)Z2V(JT$OuJ8|{<+DI>^Vl#=6o__e*aybt zX8;9p#PXv5ghVTt?1T6LK*$>=GMVrK`+I1-wBU~MS$Q6~`DG^JNqN4!-oDzNTv0KclDFd=oTUYn-%Sm^TVIRJnfIo(h=F4+U_uN#XF6)?h$F@VkT?P{4jgM)C^F@@w%76Y35=31Nuu+h0jAp*S z)7eTrWOJM;s!k3DYenf1Sp21WI`ta$$Bz!O@~lVckNWLV-+>-qV+>5R>xJ0TXg;GR z;RcUx>}{|WF^ihcj|bU9O6K!b3bmobVUK|-9l9^&g_XYpoIvm=BWWPtl;zP}(LbAr z&t90KwvuCdPqpM+dAgM;+P7Je>zB@Ylx+$pa3d&XL+ZAy9@`opb4%txD^%)^R~f?v1@4 z)m%A7f%2{189Dx8PBU1PU!SicNWeAo&aje6qOWtm&VYg;^q4t41~Y4qHQuvVq!Y>s z>a+rw?zGa&-J7GjPc4#d|of)H_-$>R~%$-6c`@Ve0SN|$_YMGiCg zy4MHGT27cv!j-q0iHk973~7#V zOGtUFMNx^a8zTvNlpaOx5~oP_H$LGs_<(h(Vupu%wScPw=W7N{%?|5^qOy0Z-f+jE z@ty(tb6h%X%ineH8?9t}jtId?E5J)#Xy0x1rmfz-QNw_seQw|iU9aj5Z< zOfYWva^LDqjF#peV*ZH~VU81sWnTCbYcgvD+*o5+|x=w14BG~n*# z;DxgiO1T-3Jkx66JpN1}!-5>z6Hs*i+(CoR(gN0#cv}@i!$$9Q(hKDQysn<6tq@D= z%l~8StD~aq*7gyQlu$rK8U#dIkZwe}OQck~8ze^>q#LALN&Obu-M znU0MtUg0Hf0@@s$+mB7`cz$j7>^_2Z2DA`A&m>6{``J)bRk-}whz7;}L1b|62h`ZQ zp2D);sTf!DXR_rNhXMXPE#GwolEszI^p}4`xQFn{&5HP3t))-lI2pS%Q5bb z@hEAloyv=K(G(!vd}S}vF$xyhGwi1|h@3LE9de6#Y!LRzhO7`L>?Cj%<9xx0$=UDd z{w51U*mal>_E;61Bz8k$^R0%?_=~T+!@>gbKAGFyOdoV~QLzhZ@GapS7nj-M$qzp& zsa9cjH$RVXQG=Wi?)k{f&Q9H`9odSoT&zix6I!q6Oy&rP8J<_23 z$ju#2c0A!~eVAQoR_?*@{;&!>Q7WF%OV0mj=Xt{!t`rgLpioG#{>CxS)LHHu@QI>U z?q#P!dWz-7Dg0V7<&9+&*vG@(47>7Gcc}BEh;7t-wK;}}L%|#Hw!LX(rK4*TXmJkA z8ts$n({q`E@uTD>Djn_zAJ;Hqp223}KSF4)?y)UOox=K6d_}a@4FZ2SUinM(@B26g z&d*uq8`a6mi1n?QfB+Mw{>CQ)59Pdo(VqQYMrOcaDft-}Ilbs>YDYVeeg9>p z-|E8ji&SfSkxJxgst(H_dy5-{)gbFr?Fbu}xZZYDTC)@Z1^9f4e}x zkmP4^8X=Y^q_d?Dgo;c06tMk&T$}d6dU$^Xa}iZ!2dWR#%orMhp5ULW*SLNzvlXax zub^5fRU;14kD7@|fvt*FI&}V9-2G0btyOM+sonoLvQm|p*$E4$8N=(;34N`xdU=q= z%Z?b9Ajj370$$<3)3Q5CK8uYW$X2@gy$q`^v zx2p31ob>1Bw1!j~gJT@~iIfX{I&&>WU*AbwuYKP3jNN^FfSYY-9e}mXs#1Q%Uq~)q zWrq>*(f{q?3wu}f3cv+g_?!}=r`T4$-<|64a{2FCPO(3qG2CWz6YShL!uUGFwfdXz z(=UgxwOdTnEjrm4bY*?gV0Ef2|wS8U=jqCas-CHt=NKD zPm3Y)!e~{>9RxpL?AU&9P~sE?HL>j31(kitrDwh%WwiQcYM@(+Nq`K&BoICvOd#5_ zF?fQp`6yX&WJIFpSdE0Q%X;5($tmaYM^a3JEg&fSAIEBBY%ANt8?{Tt<%^iTaXj-ClH#t?Vd2J_V|C6-5=i)~A zZR#2ii!~=gXrX~qrM6ch^Zl=Dn^YrRyZ4dTjMee@zD$D!x4!seu6My?i&Ljj5+T+l z*gY_2)!d3r%V9d|XS-#iQ#Sg<;4`95G!L;TZ@c7d>fL z-w*0c8}@q4xf4b&w|1>hxwDnJD9F@IYj?&D=;&H`|E5l8bCHhCvN zXca;I9tZ6nhRCy(;O4etJM=J8sn~BoBTGPrV&k*+`vjL|J;)L zS&tG+B3rZ6mY3}d7d>Gszmh!+YXii7nwVzH>$S-51E$Lf8p>JfqTo~6g$*lBHK5?- z8by_g2M@lQ^`1YmJkX{hZO)cR-!9I23NOP)F98aDVq)#gCR54|(Iui;h)rm-ha1W2 z(h_6N6Gt`w9WK_YvRd2;DyW&JQQ|Ap?rgC~cw zvkY-!TiQK(CO$1Kms9Ym%0W|2*{A>7N%^}UO?Sv8o%`|Ec&xbDB2hvx1)DE=>alj* zru0k?CQEx6CUbY;3f_Z*}fY(;+m;*IS%Gf(CL5PYJabsB3wC4`B2YcCMU5nI>%XS4JYD-7>#E~ zf6uCin(*?^7xu+P=;$S#8wN14RAZ{vV>NuI50wA+C$_N%OWs%D1;D8R`MhRy5`Bz? z%0aSyZ6fMiftdb*qWr+wMK4?HK0^odK5nwY(348{_&r>tML}y*<*i4SYEyik{PdgY zOd`t@DdJ0YxmrvDubswfmhPCyTH!Bh@!YkECRGEyk^(A6fK9MqMgE30dk4Pxi{8iQ z>0;rH`*c>otqVr@iUX4XF)WDC_5noej$?~u&piu)PFclmC?%HEiKk=~_B99=v0S=iq6ll4QcL2SR6p9u_Wo&A7z2~hzunsZ z>o;u@(VwddVGE`=OXQ;W@#TLK|HS%)!SN!1{QWBeVc=D4un%%41+rDnwra)uJ9*9E zU&82;P3jY4l03RtxLPc=D97>{(EmbRXECEOplv-6CP>RI3!=-*0(3)1%td-7wOJ%i zBSax<*p$ale_AH0q*Szr@)u?<KXe)4~}RFaPSg!m66OQ>}N_4|66 zS}+HT+6<31Gw=f24aYYSlbYWN@1iZmTXUz&T>VY8Y*_TosH!+=8zFU?~3sn z$yn)l!VGBYByTa))RHfbN)L(`L7yrR_|Iw>C^vMQzndVUz6RNvp!AXV}5=>Xxa zwca1B|H2Gn`M1R4UpwtP&(X<5%z+lW0C82@w=Rk}xjzgZvZL*wt3BAx{nf9pF>zJv zk3;w?LIZN8!X%r+UnQtg)61eo32SywxzKeVm6{o4ELZ-)ZIKlOXazc;tHV$^&?tt;kl#T5vU1ay4|aI6)MfCD#J z@8-WC94+o+{=p$Rbvus83F33G2Xsp6F=A^KNQIE>y>5~$jAKcYr_g+j`M?XzMbDrM z$Y82QhD))~UL1)R z(Bfa#LLV^?$h5`vrX&%TCSGWyH{X;k=#(0}M{E4-BSe4xH~l0w_P>MYI&rkAs0`ijs-HzWxbq1Df9>p`ySWwU%yM6XPp3vH0fHG~!Q@iVw_0mYowrPY0 zx6{NsUZzruGu4&?BLwCZoQ9f#wysQN?>Lkc(WWLrP?Oggs-knQ3DvV3$#pCl3uE{< z@XO!F+R|dgA3WC*Ism>t^pAKlhW`wLE_TUYLZ6{%9gRh%>cp%ePKoBiq%^FaT3;Z8 z^PZuWg$rE{p1YMen*dFz(W5TC;sWVkn~|$|qSvOgr5bvN5L|j$6{(6I&#Z!hdvxXs zAyDH!<>*Mwzc?U%?JK!p@jGpF>N4ub@p|9?o^k(bp@586@(HLa2s_RCk6|v0cKJMa z*KvN>uhGn3l1zg07%Q`nNX?pUtosKpj&7%Xt@~$r5zW3Q0RZ-*i!L@>l>A=+U>ZFj z09gZb0NFO*9;b%t^<-s*em}cCWb|1s$_7J?ebx*JE>`eG4gw?X&vf+aEq&a6(9Ac@ z%mH)TulGQ?(KKSrF#BIaQ^=t2Yr;@Q_o(wSg31W-<0?r zv-7eYLKz=Y*nDlMc+ey^IS=HFDuC1|>}10-%N0`D*t6rY-Az7IJIHR&#@?Daf0MIY z(Y?n}Fs5>3C~2 zzMWQ3q`2LD^4qkkdr0Nbbs<>b_&gjwq(Ia^aXrMWs*&3c<2g&1ZV7GHfyOnlk|2!M@UTW zwkwXmtd=PXU-qclAIVU;eK~+B#$uFEX%6B%pg8^k)P<@!TIXsn`!KKx6usPH#qWY! zkh8l}zTge;C>G@m5^iq_lPfmmwV8Oni`qun0mE*Gufvu9_rm{Q<7{gs3i#dL{uO0A zNW$ui){K6G$x1G4mT9uqxJ29~6!!vnHjq{azjY$KDBut$rKOhkUD4$74{qM8MNIUR zmppd=wDXnqYX!l*CusMal#Qv|V`B0)J9MchawqjCe<{=^XY+?+N&bUl?v-Mc;bL{Z$=0GZr5?CpUylSkLw=cF)|6XO~7 zzcAv;g4z393^O;x)Ek?q)+JX_o2m>CX3_F?%dAtWxl8|PQb9g0wK>{Dyf*8VxLOAs z8qWxA=bP#YXvI5Jg%A%!4mu)FJYXI&Y&Y6vQU90w46J2aD_+2t%a)74+Ev<{?$7X% zXYWMiJXW%X$o&wC`U)6mag3O4^rdb<`1Dyc7>_)QK?nQ!_?vPQ(G15h0&@PRm`5WY z)I?4VXXI0v>?WWf$XM#jt)nQ+&)|&oOt@d!8`eC1HYXyqV zu)_#b9mXj6;CBmumeEQk)9`xfy=yoL`*qc98`8KaFY9Iw6MmOn!`~Ax>OYU=vFClZ zcau$G$HC&vY&bHpBl3>%c4=j3pUQTKYCe+R{yhoRE#*R{mE-)YsVg@dn<2^!0@Pr| z^m!tp*J0JZD*5TmiYtceWXBY9p-y>hS>jNgd~4hP`tPnnlFIiHTNE(B(s<+xB3P{wGm<& zn}UZSZESs9X6jQ!XW_Ghf}6 zP&bEHWPB|4nJ4C^%y7nQz8W3!38i=Lt*3>>#VRcrWVLPRCna+Z zzxFa&tkm~v$74JuC#PY*4q9?ptVaZFrzYOSK2_oWFVPJ#3QaC9qgi#CRx+&rh41G+ zvH`;t+Aa-sT^f@^qGP#qB}5F+i4_E&>og>BtLRW$0sa0Pu64j*ePgZj?mw zUTI>bWg>MJ)I%F_l!tFnFB2Ai*nd?>2r8U$O@6dEv=d`j`>b;Sm(9?_uma-8LxoeK zGRsBiCZzuLo?Kb0o&^iFp z?{j(p#ws{Sh8BDg2v8zOdrFiSlg6fP%m*r3Yt@|O z?xat&i(r7KreqFQprFm{Le@q#xo_5Y#)LNQvdZM5(rS&Y(~DAHX0M$B>e@OsR{%5z z@kc{{QqSa76$){_Xb%+k`GF4dhWmfHmplP#uvT&AWtj9GhJR4M(2t`83uGMqE8G~T zGJOei$-Li5M&|MCTa#FtvcZv-Cs~ia!7jEAHNJVqSmno03(nspd6qR z6G6SWodzQPK6}@?x_C#yTX2-Ek7~Us5Lo9E%%M0KwcavLOZki@vGR*sYWRH@@g>2GlZMZW?R^1^1_YUVzdoFDAi@4_)|nP;t9WitYD2XpqXyB%5d zEhX$ec?Zh`Ouus_xGn^N235O|FdsDlompq((XHdL+?~q&h~UxY^7TRG>3Xc3BNtFv z#YC_FBH0=%Q3HUcy082%@^0^Z93mH2B02tOS7v7|Tqe2UAnQE7{ShF8 zN)Xip?Q-9*G%~1p&qdfDpeX3>vgF9TLMmwGU>Vp8;j{E}8~ZpuiCexdj~OokN}=P} zQ^?|nYkM|7eq}cCq8Y$uc0B+ARR9vn|K0T&!8u9ozOe1|Q-zb)so#-6J8WzhLqqFJ zl6PzJime4aidtkU#fYaN|0GMm2M$2<#Egu*PY~jNhITOLmxJZMYD#bwpZQ%(n-pYj z?WKw<`BU5}6`@K>@G~Dh{WB5XkN-mx@GwPYYY46A%&cRr?3a=CnlSr%9X(e1ch<~B zE8H!9T$SYAmB`t1v{ez3m#7>2JLiOAFzVnrk^;kdycJb4WfP+d?zYI-2uRmB_(0{c zJv+pF?Sed;GV1+eKMuvd4ddl~>IflMK0tA;2lg)p_*yi}p;SQyh>1Te&X#Fy47*`)0$_y)e;DlOSynqg-4N34X>R3Q*6trRtw*{V{Q#3gcXA|Yd2 zt&(eENq*SrdwW4*Zb3ymz;BmPG!X9UexIZ#vdyPnG~xQ8>SFCjLbRF@{a|u^W#Z-f z#yLGd4-qyawE3=(^%%$&V0o7(PD|?*a-7Q`KQrAlCj9b z&d?H;j>j2}Hl7Q9u~~i_+uKSK;&j7=5~uIspv4vh&{?bnV5X=7hrYcV(CoQ7Hd8izkfh~0OHIul>+nJS_)q&FjpTqlcfD7e$v?=CG+`G z!uKA)3jJ&*gwEU_0))TYOB`JSab;1ldzh3ymjgS0`spyRO@Vl+vAF;j=aZ>tPcc1a z$_a)`Wwt)^&y&sqf`6t}{}htMq8q^Re?|P4kR%)7;|{efa^0Vzo|25$PH8Y8GX0V4 z@Wd8DUS(N6wEG^na+}IrWO09DbEF5kN}Fhh$mp590>amLQx;fx*efu&vUu?_``5t+ zmzkK)FQ|ytSyZJO+xKc|jX-7 z1hjv|Y_yrNR}jpnJP^=UClZt}rL^$gyLS~E9yDx+#G*2Yi^g|L z4B;x!>$pin0|$tHBOw6i*NG9_I>QWR*VnMkosiD8zKt67(X-}o3MBY=pn|$Ye znB}aNShnh*tSvWGPXczvDuS3ujC>^MRRARJ=AY48KSIWKCnpjyknm}sC#gBWRomC0 zD$QvLXox2gP$DDU&wRXBqxtLpuo;|LGBfS=&rhh`9ER$r7p-ny&MepqUtMzh?6>3) z9CIl3jYGvP4?rW+szTeZs}|eBy=GomI9O8*PA=+wvYDGINsjaJqhOZ0WVtXnXoqK3m24ei?53xG}{Wy zyJYwst*4fH!^~4^03}H9ESapG`_EedpL`~;P^(+{Znnn%PKSp=!oPXm1x}4jaa5o< z>?Zf;A9jIo*!zjBRLhLLz^e~1^xuk?t8`q99ZUJ~11dQ5DeEmo2m(k%*~Xua)B0bG ze*d}i6qr+Sw|!OC$ew!-1?~4JgX1^=cQfqMZm7M{2Bv#}`PR9N^g?nG-9be5C z*p$0n96t0I$qOr4d8KFU&~WMXtm@3aa|5YEbwgV_=Q22ZpWJAtX@UQs`DB(JoBI6? z4qfTC|FT^^fC6cM2ez|LHT(oIaynvyysF6ohD+R7i!dCp;j&p>oMlGjt%3Zj#ad@k z2cVqG4|j=nKwPMLzW>#>f-~`j7Fb$(&3_k84BW#iA{TA9Bq=uB4s(LPfab_}S))`C zq<&J@;8lw!7;a&ku6>a`caIkmcNIlj4ywOf_C?>KooUpL|JRCSU*ezLCDOuJZIqH)~l1fZ9S zT#bL0L@uk_EZTOI&j{LjLl?ZSsgY*W`sSADQz$^C;c8vltV@yYqOLxUtbddAMOBAXSwcKeODrL>$nEhR#f zwd$dSlokMGW)z@p*;6T1pJz87kkn>qr1jBS>)eW5cCA(>L1l+imQiN9R3|7@2R=s< zgmE;RmxrkcB4@Rc;%<0Z3y}KaC0I^;PPMM!{WeC!vs_y;7UrU<${J z=8QiyxE^vcToXH8eE=DBvJDVp))JSM9_iOytVA+t24JXT6Wz6Q0%eb8BmNVB>@)j^ zD*^W4smZbjZ}fO{llwsI0CDmVwNvm9;0$R0BNX~I;NEJ``xngCk<;{HB{eP(bjz9M zyPk}m)GivM9B!N#>1A~&W9+_J4pASq;N9)}6Ru2_ncDm15i>n5E38?FR%lC{;b92n z%g{)1X=%DRfiPMaPgag>I#)HoB{KWkEYm~)6|IlC+6iWvwExjLj0|%m4-mE4V0(3i zyyZ4(&i2`Z*t!pmr>YZ5yt!b&8}00BKIvy+rnc;(TKkk|yHJs?a?0c3Rhw~kN@f@< zb|cwQKHi<|3w0773RYSICy zU{Zq;I1bE6svfQ`{bos-bKMw!s*083(mE0<9td5QqfYEY^CdWhBT(B;-(CGq95=YZx%z&5u*WK-Hr<+G|G!-d=H`aZ`A^_hd$_H(GIqa7Umjt|} z9H7IQJS943Br)(mGqK9MPZE!Jq(mzStz;HWSJpZ6iVw87(>@npbJ85HU16t|XI(o7 zV7Qy4GxW`#5BD2oZf@J2MMOM2z!et}KfJyIP|`}6JZz#?*bYRZr`08^W1r~ds+y3( z<9QqNKBA_UneGn9gS7keRn1(yii(Kd^vz65;t5}7r)-+FStFFE8bk6y(``z=VAAZl z=HUw}N7e&Xx$CzHl!)LpzJVE_R61r06%smVIS&xKX%pMd zUixe*uGctC41RnpG?XaOs8=RT*K)`kI_(w=;-@2K4ngJI%%5}LcCj3YtpS|@`-{Wn zyGsckRCeVyDo#^8HSr`+qG)9ZwM;DczHbu?B?mg6Ki_VFm2aLzn0?<7Ga`Q3A{VIG&irG#_)6K6yFeeO`3D=6DO|H6rLtCukn z?pOi8W1s&c+0|4gLT3Ql5Jx=}-I*u4Qb@F=|5x0r{5=$+8Kzu}rA}@LtZ*MyyCo}mZ z$D1V@-1I4#w|KNcGMAtDqKk<0^`|;flYM-1Yt(VX;kp7g9{DV3ivGRv)HTWC22RMpbGa9iuX!N&!y>=Ze(^yN;Q1a-BpHz-1o_49VgcS$wXcjcK1!DC z4vxse`8vqiI08SdIe`kXQL{(1KG(=t6qi z_FF&aoA-MhV-~Cg`bu>+iF?{9kBgEN@n101I>s0JnIyf=7|;z*GMYWNqp!rHuLM)v zo%<{bRaq{0HBc}YpoDUuyq+TGb3UMh6Y-OUMc0GI+F_N26Q~7&5JS>D+E3U`YaySm= zsN(5=yfoZ;{Svya?&x`|^4QVl@HlWX^M)2<7npLnjGK`&H;CoVP?@soSJK|Bh6=c& z_|$RqYF$b|H#|okMw0e=cEZgJmEgr$dsSO!fZKlp46tmtb=}$*k$JXk;0VR53RFaj zl=;;-ElScIR`d^JSg%p`jrf$4&&u@X$Y5{$~_3no|z-m(e98D*OA ztzWP0xa_K@@{3NavW_kno0W@=^w`E$PMKnQ%|eU%R3K(&sGptChmiLwbDg4)X(JAg zKy#cXR!s|MZr0F-T^Lv~MHIU}O6ju->xXkRARmQiB&vz>Y`h?!RX+|CIBRI^XOa+A ztJE2D{53*CGqO6w162+d*x|LJYy&9hNXlwubF;kbnqwh(nsd0Tw zzo~H$A#ikoC4?+SbStHNYT5nXDBSE)k^|r8Lnb@>ckJUQ-W2repA-b%Il7fQ33}m2 zyvokqrWEmrd)ljWu3vHbL3!q5z1nieB()T>l4~`XbKyAyHQeMNHNo`?mrXN)x}j?C zu3F{(-b4AeJwdPGez6_Ofs4)^-k$b|7sNRg#UEsG**bsDcyxnX+;=mB_*r0$CO^-Q z+(S=nV$3#^q!yZ_C3h4gjk>E<+3U7*6 zS3>$=yT+=r9Q>}Wd|9QNB7S}9o-S|CBW<7l&{X5GfmpS(!}yp7|~uvpBw+_vR32 z8TtAH1~2UO2NTcN4<9?-$2lV&dshtRi}ekT16%1`V%MutAM5DZ$1$5Qy{x>{mwqb$ zj4z*HOy53SJs{6eWHUb<>j5#&I*#?u!<|R;2yP^+jCM|(s0v05-kAa~;XrBZxBX$i zfLvU6*xgg~&^Aao8ssIhuL3tk%*|-gAkqu&r`_n{@Ui7%LumgL`pqk`CEcoOJQ=-@ z-1jJi&En3JAHvZ5WG+T49I+H=jq*gL-lm=&h$JjCR2i51b&J;sc7|Kau&kfI14e4KsufUZ@THE8m-xFzgTrZ@}9P zaawUXCbWShR%+eVA7X%mS?Xwe1k-kT&UrN`XA8d?v?&A1OlFXL1O5Dt*9i@c%EXt3 zk)*6t#*>{bd77N2z7jyjs&?l)M~NOeE(Mv$>E;=CgXK{R^20}njLnNnq8q0(2~9WL?HAb zo>IzOeMcNw*b86iOm;#3Oq|85$?TL*x49g;Zf+iT9d9q&Lq6YUlL@>F@C~?5v|IcU znL0b|!?^H6(LE8@^_Xg49~Hwmkkaqgy6Y<6o39!(n8cY_I@svfe75f!#_K-8s^ zPA+~DrBJv?_>G^G$d6MlVX=QDxVmXyu~Zd;W5GASQFXFe080c8^_QKI&5J zMoQdsn@A=)aU-Y6_x0w|Ce@)T1#r?{-I|sJY!E7-uGt;cVP^Pw4p> z;4HL8xA#jBP3$DV5zdW?6h#-@k$G!>Jlt{KJl^!4%{8-AGg!bjzL2M!b!Ox9;gqO; zdjuCeDQr%ohSJ3se@V;>v5^NbDF zB9~ZCslvr_uEZx&b4+JO<8|o2J&JgIkmQqhQu@*sN!2QbwMb6oNvkwfF@FZjD_E3j zF^Lsj!7(B0?Y_M_9sc-6AsR%Y1{ptE3y%^n;9Z`qK|dl(BK3qavCVb9u$`(P5YaT* z4W9)Wylp(Y6LD>cEr^{PD6<}4JPo$f+bm4c!IjF7gD}C*Ggp!m59~yLyBt%EU27j0 z)YD$J%JgIV?SIQom{+ut6qNbOWdv5%@%<(q_tJd7y$i5U#$WNjHxu?;g~mK;Kb^qy zRNz3Yf=#$`blq3hV!^=`#kW0v1R1~KWL*n)^beZbxEU?wO8pl4(Tq7rA*xK{I`y~f zL(4G?ECOUXr}GYI&Ux~7FnQPrm{AG@4^O|x(RCoDITJ-gJ`bOzqu5#{X;6JhYU+0Z ze5l+d+L6ho0a^7JtMKeujp5hsrLozw>ffbL0+FLYbjSerl+_Qx>kq%{oxfAT6i)l~(SnKSbYAEk zOMFZO0*tl0xskk<=F>T=1FO{WpZm=sbrpt#t06*-a;vaNy_E55rtG1ez>;?MLfE_4;@j0iM|qzOML++9Q@`Zg=B4~S1d@3+*N4_q7=c*$)H<+QeeHs6AC z*t18o$szG#H(~Y<4f_PjbwhGUK!Tbnn_a0UAq&4*#@R6)Yd&^2?`SdR96=w5Yh`D} z2b$!M?*D4@bXLPRV7Q1nY6%&CpXs8OEx6zaU-o)}Q1Jr-YgS`#$7kNUOp&sx*)p)( z#dg#a9A(TL+3|(U<<%ymlcYp7q;=+~>qlz%1840jsEJA18g0uWz4RGMwW3VumT*@Q zvUDz*4rPLEx|o3yyD24fZmKNT@gWN{s^31no>flr_gKSew4Fg1+mG2Mc0$rWP7bm% z*X+p->P4-Jkqfck`%wOpW#mZ0C()phG6^_;C;Eprm8#VqFY@AGZk&4oGN=XGoQzLV z_i#={zCULpRa2l~d{zAZc|W=3!MZG|n1 zlu6&_b;OEtzrI55FGuj|;+%OHR1x1C9Tsq11W_+i2?!mB()K0xv_2vO>1s%IM0?%c zml)lwth=&03?;ewum@07i_d)uyB?MMY~gp5DrJWA*_=LUK;L$ic#PZ(GEW`OYF4dX zMGow%yl?5Wag#BDNqEz?Yuf$`PxxJD3`Vx{?%SzxuUv&&clJcuue}RlMu_*zbC*HP=iC*V*nUT$Q~jR4^(^6Fw8e8V zy-rr%&=*r|O*g|CZ}8?m{ybM>R)VHFXan)GdkV`Jr)G?3bJLD~Udtu-gc{kEsCEst z%5;@BZrz71eBd=;zIdSKa%X+${4i2Ho9gzA_`=9&8*kv&jKkfJD>=#XCy#C6*Fj=N^#^&lp;RcAzG@;UyZ&Q@B!y<6X z`}n9?TjE9>Pwgg4!-wFr-rFv^ zys^|Z2(GK*ADz5seO*@NuB`|6)KPwL4+v-a#4zcMvi-b+QF;9a9~L-eM&cIt%fK&1 zS2>cAp9?JADfR^%5iK-geLdlV-hNPtjvoVzfPrG4b?Xv>m>tj?u+VUNNLq8l6lVd z<1BiNeD5jOsbm0@R)Wt(Iy1OpX3Tx5MGU!Gd=|=V6v8suI|X5PZ5b_1!~M_hB$_d= zp7Qm*UIOZ=4@8s-fikK6>`&)6^~BY7Lr-{Z3J)bERl)14Q6Z60I61@u+?i)yEyS40m%{Z0un={Mtc*a$lxIB7fe0A3JK`(qtBIxS; zM5&S9N>KW#T<46gK?EJ|j;$W}UIml&;xK{l-u6W3k zB=6!9%UnxQB?<&fqCWBFsr8VV>)?Any7|i(0sTI)`$k3clX`E;EEQ!07nXH@1yEuV z%pAg&)$T33QXu3Pn)dn9P3CTCgl{d1BuXR4bARe3bORp6pk;}CABcO2Xdoa3GdVrN zKYwM$9#hFB+G-Adx8plbaF>n|FjOWTJ{h*(DqzQG(&IRbx2`32gd?Os;rG}_^V1v1 z#m$fGaHAcFgrK&i$bGr*v7q(tG>C8l!bkORVB3@bF{h9h;rbc~JBP9Nx9b&Qm%zCI zs!Ds`70PrMcC->G?o1_C-WPYX?<%i*VQh#9r%;UxL^CfkU#FQW)=zY6()Pq$&6MM|F%ud*WQXt~794kguoJAE~-62I*f zcW2n$BV>*Te=JG_iZ$Kkx9-yyi*gl79TeBqa7X-%HC~y7+rOMAIeDw}S?gBX)7>{Oymav`$hV*t)P9lyel6YN0~NEK|1hhLPs`-9!WuX$fR|tC4RS zWS$vteWj6j9NCn}oFo`gOu`Y%bWUtgBTrI^K}akmh&@H?_KttGvY|!osV=YN7donv zx!nFnf2E#X5)(4;n;MRwah}6ht}21JBEt;h^BK^+rP5n^Gebc<-Tdcoi=CMp@8cDA z8MTl)&e>S1{fTf~XFXI8Qupe5Q;JThw_%Xh_DqDNa@j{*;kZb_k{EX=OC$@JA&0zX zc7EO$jOT0yzJm6biltPc_8})n+$(qDOP?`Z-0kA5MXwM0Ne_NKrm}qLr+1sVRL|1- zL&%TNd=rF?f*;0`MbN^(K4v%E0{4DY^EmqZP#v%LGY z#yCqO+*MNo5G9Hb$BJ6^c|njVXAPkNvDY5Ap8Q)zjl0$dW{ zE$xc8Qot~4USL*O0+HNAquqM;;DGeAv=k&1ocJc!81!^qJp?P4;kL23JQD2$FRN=Dh_08<0hbYqk9|n z5|I{K7D-RP4AOvO)4uqSnt%=MfkhX7v&0uiW81jv01Uy1MC0r7kHG3|zyh{_kByR(wt zpKOawDkB#8RpWRAH&FX2)%PgTg{0|B{j28geaK#+H3a1!(Vl8}#n9zEBE!H+j{25I z$D04_ftUP9%8souw+an;)-#jA>dt{D2vOm8J-W=51#HfIZn7UlwhH7ZmZ+A+yZSl# z`a5x)&<7VmD8}YWRb3}1-O`oOzyhtmv+>91f{Nzl+WD0_KINdP?|}3E{qvrnqh4XY z1dG%pU>(aSJiWl=R3UkxC)FqY5EBx>$L6r~U}RjBV3{mj3)}x5eIX6Gxgk#8jum9; z9FGF@Ei8K$H^ux58*TzCFGF5e82@+oEpCi4LLN!;Xg)O&g>b80oFI>pX^#=cN+gbg zDMr@&7PDF4T0MS&E;zR5=Gu^^injPwNa=;Pc#p|enH{VamSd4`jZg6B+MS`qS#oX1 z`PvfDTyOm^FT9EK+=O`T;W0WY*KcVxq6ZX5u9tKeUK0GUkNBB97!8k2msp}@e zI#75*gI`gkV*UGb5`5W4a3vz~4NjsBt(*q)=)<=)4keuoApECU#8?{Bi z<}BqTHnaRDE1V4}a||6%lh+y37l7f^;c z%S-IApU0FC?6~e*d|6r= zlr!2!1IcLtQKLxQACvxtEFOA`&oBbcA!@0})yb*^`G!*KG25$Fr7`8S(DPOqH*NA>6d@4-@m^bH|AX;&YJ#RdtPNGR$Hfa zo8{|c+N5q$`PkR_XwO*-uB19%uM%^2jWQL=%qr|zef>E*>5{rWSpNXU7rxU@gXvzc zA{{MfOH0YDK*!x+`XSxfZ}}PDc@&VMZe=cCd@BgPU*Et1;^sJw-{{<5al%dI@&A_*B(uY9m#_1KJUd@ z{m`oKM2?r`Xnn#mCd+h(#+DGBp@R`DR9u@`a6YAU3o0MwSC^YPAhXdGrmyCC|LPCG zTm9u7Z6MNssChEwcPGzKOIJl*asKE7dU0x^eb(Ppi@EcpLmx(!T8Yk}ttrN3=COa1 z3nMm1)6NlPa&n_2^&4yI3bH<}k5a#`LIz$LbN!SJ9q`f-zqSt3egcsdzxxoz_sOMv zZMQ}LaTTrpgfAh1!-zvK7pOy@az@|Rc%DB+iSY?J+2zJ>eAIu(9r9R zRNcg-&w3Qp&`0g)NZye6JaLZOA;S&imjNBWW4|&FGn9wlg5|gK49vQIrR!DNUHFvD z3HoPH`CdO3It0hfPeT+F2R!Zj%|zaPL^Qd(%;bex=(BceH2koD#NO<&;ldbs{mpjP*#pk+v?a~y&ILJVI&0>W0bvyq^SWEbgq@nIY>QD^j z<&@BI%UY4Wi*eD-*v3Vm%f;QLsD{>gdZmlPB>GEaAnE0)rCeVWoKu$iX=)Z}U@h!h zHD|7R5&9=>0I4t%yc0SVq`EF$zlhLrAZP>_%uN?N+RySuwVLQ-j@L1LskhaukW zeV^w%&pGcopZLOu*|Ycj_gdHbUDwJTlPIk)Ur^ECEh7DE{0XIx24WDR#JsE{x7{NkQ&{`iGXZyrD6Z)VN6dmqZXBf+8@^fwX!y zk0OE!*HywVg{(kVCMArKUo-s2w z6b;x6>gZ*W6^Gk5boZyQ9Z!%hJ;_{;{kw+sw7$>6<@yP9D z2)u0gFc^WyLYeUtW|HyVTHlW%7KY@IztXbqO&3B6E+uyO`q;K^!lQo+aQEi>jeB6F zgm>UcF+}r*=CXhPsy#{kEbrm7)T$}C_|aU{+n%#%i-;lPPaB+~BDruY8X)7c43Qo+ zh$09#)lj(4ZcPjI6$XWeNebeYm7u*4tiJZ&M!BlZBj5eic(IKWVyT)Tc22JaOV3e& zjbCL%9Otpa zrvp8a`d?hk2~w~7K>HBT%qwfH)Tx~Gx25GCP<9-1l7!WDAtITvc9>rYNOA67z|M6ECNu&w-_O9)#&AQIp z>##i_uF+O#KM^;1xpAy#^Xmie<#)%vryHps$-~M?-2Az0z(xurO#l97$)||jaN-$$ zkvoX++DH_NG}@NhdW`l+vCw^uJ02iE!%ArAZzkg$Wq61Qk;_Mr#eG@T02UPs0hhuh zn^?BF%Jfp@0IBroS#zz;TlE}}GTX~WR#(dqA=`DS{_l8a-tWTk&E8PxW{K6W@Cz!o zQkw6jJM-5|hIigurN8w4L+-Eq>Bg3F{O2j~}t3VYKV z^@=@Dtd)9Sqr)g_d9iLSEjdC{Qyj!d4agRkSm;LpV zgj^&MNVR=!MfQAW{wbkDB#S|PJ+?}}MJ|r=X-yW~q1Tf7FYlYL-I1bS9c#y*T=Er$ zW0XI?xJ4FceGm)3(A+d|KoT&doo$WyxJf2M)%arPi}3z?iDf#BJny9<11Q#JIeuk{ zi-Dt-Y%q{zZyos3pQJzEHBf1W-T87Fox}2ck|r8$8p-i4Phi_hc(XC4RPoY3VD9>? zIG-%CISGBHxrPtpW2{&&9a?CYvz1ueKlEz;;bz0}!#XJU%8zS840S5jth><{3fqVntl7V`?ghdJOhjd5vy^mRyp+igsJw z2N<*FVmiD1qu}&9$xsvqVUqb+_w^J?S=pbrZml-v8$Qd>`$mtZP7%jW4WV}b6N#kJ zq$2Z+kKjkClwo)b5aW+=LAM-lJac@FO#hySQ^PVRts`d59M3bF9PM1upB8#}=41c< zJi;NFRTirD^&iokdHI8pN2xF=9bPCXQ9%*UlR8v<#N08&Aw;Q=mj?wEF5J$#Mw4^b zO`zUu&e=}kC~35$XJLA&aRkJGdcFF`8X5ddST`&yV2FaV)k>`Fv!zHLI-Mj1io=h3 z=r*tPFtZ`iXh~#3RaR`{xZQqFFX6CI-<~r*ebq3a`*upSi2X82=I!XWS2=^Nlrx>!L19bZ;0~vya$H+? zE#F3w&^Xc4RY3-(=(hWsuf=9|!QX5^n-b3Du;f>EiJMCA!E~fQ7LH@)d`^~0z~jSJ z)25rQ42M5ivs4CqcT@;}N6$X`Y$>HdUD^#!qL$Izx7h7h5n(Qw7nKbrY+?>?{IPIEa zDQ0?75Qq~~C21uEqwPa&&EQ|I zvThlBkKh>d^5uwXR%jp}==@VJ`s>jCBkO#1dOTL zAErS5-E2ui;I`EecNE$_W$$w}CBk*y&?Ty9G*%g6=hB*^4$C$C;pD6v{HP_>=#Jld zPd7@vb#F_UUzm$-`A@f)is{03lo*aOuiM*9@bohuxlW4v&P$dVlhzqtPn<4>877s@ zA8`;@fM#meiyC^bLbkg2e|PAw8oCxUuTjj&pE*(|1IqbGlD5HdbA&=tb4rV>0foee zzJd9BA~z?(OfIwHRC%QPt9j#N$$GePtmEpfZAa(T?M}QjJz^f`S_6-fZz3nL66okS zTxPX|i1n25kyaa-XfYnHk#5=E!@a7Vz82QKi%^fY7ALW>@3vbL3~J*-Ta6BQ=susa z!1Z2F{Wl`^Q=8wwr@b`&2xw`MahlwhbaA5Z3|IEWd$6Tt)6*J$%XoOCbGboNhAhe+PQW@w3`6rHnyqO={E@{!XsIDShl)v}m7J36bEta-F6B(ft0t zhai8^L3!oYXRLg1G&-qIOw!Lly)Sq%w+S?~`2)z^#7M2Oh8dqkLs@JSz?(=bFA6c; zM?v(w`FGn?fsJ|BBN2=vv0PmK6x_um)IYu(%%Gq^^+J>;-_=&j2P3|*$q_SQTW|z6 z3NMICFBiP>ZX#LiW{US-a(oMqG)nQ!rL=cNWhG0G#e9V^U9#RcZa^T+#*)8zNm(fY z3@ehIUt$2~Zl$j2HA@&hFdC8ygKnp+qjvMEi9?MDgzGn9tFz+2 zGZJNY^?g$CBtC?!lMQFQ{KLPB7hi#8S&?)5-yhl%1;5aV$}ok98U%r-^+LSPQyazW zV=*Q~4XcI*(u7!ls6Y0byY~j~W95gS{cuucTLRfiUlat7d@p1VROMC(*rrIY$s&(Z zRQyCYOc%qkX+0z5Et0pNUlKr7i2hXtBahfn{0FKJ<}+zR$B1_-W3c!d{f{6V_nx6V zQoyAadYRpB__E_j3)Dd|>)35SX!KjDK!8E&je6yr7;N;mGACG+5`mG_yHQ*B_Ska+ zm*QJEZbG}o{26rL=4KZK_kFs+W++E@qC+h@)_E- zX)YPNMMZwTS2cYl)WW1yMY=|sT%<-GaJDtEQ!9ay6gM7r&aX%a(*!-ojreQ${?FF^ zUK-Qn#phsp_xzP-E6*MI;5GvtkkgezMKS41zMyUD;msDkLf(I$fT|iT-iA2cSOeWf zSf2M-P+#6P!(ybNdZYH?&3tb{2{uBA(DWb9Dh1t=7yHaVTdTl82Sx>jB>R6Ns zh8t;TmS!zF?Z|diECsvpty-N;7p=)U@Np__FMKFRmc;1J@!!UN;4ju|;^XGT-wv^u0Kj1g6tN>Ba@6@q0o|htI3Ze@?WJcH za;s79teH~DMnJLXKj#FN=vd|(mnN|DQ)Qenck^mo-YOA@BQEp=N$#r@o1xz686U;J z3qj2&d>+c>Z%BgV+UTQpM0Y`z^X?zBTn0E{`>t>H@a@A;sWfiS=t`hlS@S;e-uqdQ z0(#e>TtoWU3e~yRb$9wPr#(`;;Wid1TQl-Ue5i>eV!IJOtra}m^-z9a+g|uFYAcg- zjbV-AvkJeeJfK7N5ka1X5HI+CXTl zbJE@zD1?k#j03w=s4jl9osLH(G|lAt2(RA^Wxt6Vv2v_^WS=X)J9@wSS9i3fwc>ED z!BM}!Z%cZ1$;t6q#kU7s;yKTH{#7CdY2n{=x_b+(FHFM;_W=j^-tq&!0}m$gy0Qh- zC<9^P@ibGTlux1OgRS_*ahS^aW()1(H!?C@H2f;J{c-js?mbd0Aq7G#p_u z!R?H{^x9Y^B#kly-C_m86WO$DUT2gV8~)2liS0bZV_YR2IqEJ?S2we7u5CBnh(Wf> zMRFvp+Q#ld&W?1sWh>r0Bw$+@(RQ-F=UrtoOS)_YD% zltS~1->O&20%wxG`Ev=%!Yr8Fw^#0`-JXRm8O@F@f<+}I^1$4*(6Qj98tgk~=Mhgz z)AhDS&LjN8o-S88beoJmV1~RT)~~>PT&AA5ehfk8Lm9pf zTRR|edNa$QdGBVuD3?lfw%3jsb}(;$TEcaNuGqGMqn!8E@Rh?sSBd8v?zN^*;^xl3LypXr6`yn&P068dUN z)6Sm{9~hVj2A&eg)Ed|Z$-Te-_L*{OaPk|ycy=*&p@JZgf5==Pl^eZ8A&JCLQk`7Z zy&tP3Y?JVlClP779THjSxchI<;y=}X7$00Kr(-uUg_Y0noA7v+_VyR7ei2@*tz{n`i0q|-ShB_KqN21or;Dr`C_9-+Tyx(Dab_fo}!B=cXs9hM?FQm9Qx z4?%aCRQDDi4LHJ@AIHwesXU0^XiQcQkzbZu@EOE1T;O2;Pa~Q?y`%SQzgIa8r_V-_ z5%>`{PhSLj2Kma=dzawpzY(RJdX7ywO33iGoj4Rc{94jQwqGQ#!hG}{796oSXqMHZ zT%MqLVZ4yXO4(KoWC!!3YR@eHGY$Xeh5p~>b3H4pUV>vl=Ru@P0;B?UK}}>MhP{lP_U_oMHA) z8s)E=>(9CQzo+OgkpCAjl6#bI=r-t=;HBCl)v@#!Fh_nN>fHc)wPg5`3fxOm;QMF@fzO1!QR68LA|qq+|0&NbIC1` zXtvJ~E)8gt;l&6Jp?@xN^=H4fX>`o}AY138SjQ;`;mueUx%S)w*U}mn$`PW`Nz~?; zosZ{rMYf*=R+|O+HNdhT$J41Qx*vEVT$Ea%wll@dP+~+O^L--SbC&*pxwL;?s7#>f z;JwifO!xOwbvGC}Rc-CdZBHMNTEE$(OxC(7$MI)RF&g{VT#+xpq&iA$ls-&CM#N(Y z%f>1@B3PG}=F{Zdk%C^1fVFzqn|*6od5qzuhW0_hr}_|J68OI86|v9&SxQB5&%Jfr zAAQt1Q%7E@+$A{EsH3`*{Ya4|SptJ!lGOWsAhXv^mqWJH|9<`d{EUAdE;Gzw2jcH5 zpr7}?_7@l4LGumH8Lb@1F7oq8TNauIX>5I^9(AvUkrS7KM@EVvX0z>Ays0!_W$uCpEJWj z&&A*a@%^{?AA7G@@SgI#g3rm=+0o-^>k=&k<)y-KA}b~cM!CrHUvwLqp$DvKoFMgBMU3&vnaq4VrvrxD%Qzj&%!>C5(5!u)*dWh3OU;(AHB`RZEOA(4| zs%2?y8dI1bhU$S%YX(v$pd>OFwARj6>QdR1DIG0we4LdrZHh!;-g!hkWLus|)W%`Z zZ;m8ZZLVb2X;qnH{_C9nzjylY;su)$vkq9&Ord;+Q4LZErGjGIl?sILxCOp-g1CzQEj7ypY<&;QPOp-8anOM<$OZavn@tO8Ozz%0K zrDRF^Wr{X${{te3b18cNB>{a?NOZHb@TNepjJI|oF%tdn7to`eEDnb|S9ixV^u-O8 zUB`FN89d1)CYYVHi^r%?r_DEc1q*-oQnFOw#XeU=kwdXG8@2!)#cniAZ_hyl3pbxr zVsOdF-E=QA^xW5}zcbVt{**UkJL;MS+fw46ZcA?4zlGx{wfZP%&Gx2Rze7n)T|Gc| z<2JMHVtbK&VobS0dyE2+jF~NxfM!Uef{>LiKHVendsYj%dN$2}e7s-zQDdoyJb(Ox ziaaRGbRs%zlZHtu)&`$tgA*_9{bNR;u!$5hDs2hqMoZ1X+p58&sQQiB`yG|L`3TaN z(<;@3c}`rRmLHZjOhVDEju5iMjW1rmZo)bY`029Wjh@$BmTOHg5DkZ8lLJP0@Zv*X z!@md6h60;ElgI0RbOODSFT@+eX>nx5Bf&p>m^VO}Ojqyh5|WcphUM`Khey$(7t&H# zKRvE6P_ZJ%v4Nht)yKU&GBj8yIO^TQ@4IYEoHNpY%Vti);FE^+N_;n|^dZ*L-@jWW zQjYyRiX|#8_5|-nU(E74wJs{ep%M8eP{jywsAtp4B{MS&Y1Xg;Bu)wj=uU@L!Amh3 zup9g-+%%-ng?>E)K)LlktBC({?8{Q-Uih~9%K&S;pN~H$xrK|8NQ{+k`$<6^gX!as zDU1tY=AzDNZLV=aE;u8Tn=I|#q35t^C2vPSj^8EB8>Cn7cP8F)&VDBg-SAFIb>A6@C0 zX(ECW^@uYlAt$SpMc1fBJFVBE2UDo1vS)piE=+i_IHRv)n|xn@s$(C9d-R)alJlh^ zBtOEqlmgmffpSy&59f;R;A7=+wR?PVt6pfS3bk-TfW2mI3Z{?dHejmk@BL@pthZ_! zM^TZ{YJ(=r$5NfQuA2)>t5r=nv|Y3%bLEf+*Kam_8875hyjh5N?GiaB_=GdK8_EOH z?ubzbtv<{YD}wK=N z;~!eQRYH_;3a5PfRH0FwS){DrVwVJD0%%rSI_;7AqI*NQI^BW494+ssEFd8zoS zO}rN8jmPe;hzhUMEX?K$_x>@4y?4V>K^H@QGrccD!6(htqq3kb77Za{&d%}RG{vk6vE|gbf-Xt?@)6KTSobk4)g?eqh=CI3kNa8R zy)!7FfqDFhcB~a#jqycU{Xo_>$AhmqPDX1vDJ#sEGYdi~6<33ts@&gcw~P$gU7;^m zC@KD6V<~3}DNfhz$EJ}(N+`;$gk5PvVJAHSx_QV4FIXT~BkG)xr?Vk4aBPF=d!&19 z%+J7zY;$1Jib9RhceAy04eUW}N81ioGTgpRP{wsW&!@U5c^4f&Gf_@wyJW<&!_y<` zbGtFbOg07x?M2pCTbzbTF=Z4I!E1XizV~}{R$wDkD;F!Gw%z!#?dh?a*V)L7R^#rs zUzUT1%PeN&`w!S@Kf?l5O{_MohAHser zJ&Co9tRDyUuO)u=4oP-;q@Z{wOuVBahhoSrIFwkit})S%SW*xzYQwwk%!)=I*Fg2#*vDoS2E~ znZ9W)-G@FEc)apw+#bH%9-1ILfKD3+s<;3^dVR- z&h0!I&Qm8{VSI6CA2*KEJo0V_pDwPpD6FMHY;U!s=kl1BJQcEq$q_;v?`}f0>RJ|0 zgE5-_i2(3!Mt*f47rUmxH{K389sZzvn}kc&)gt<_hud(?>qC8xWEX4==58ITP@O}3 z)~MiZ>(89Zsh-YbMJ)`w922Kyuf~<{8FilHtM<4(a)(7{6ssV&J z0oP~C&y`!W3W+s6Myw~_b6Ea()`8eC`SzP#651y0s}F#*e>dm0@N&YZ%Tuj zi%?d>Sn&Mn9CAKdu~j7Y;E@jLchzo60wlPRM0}=RqDKso`{A17g--VfX2ZLtOozU+ zy4i&B9KK_7tJzwg>AMknpT}?bX-XUN#Y$XRd-mh8w8s}x#frY_O43y z+d?SYOa<3;C_8?P*7iK!$ukNL-Fm8TUZvsc@E!Z^`gsoyg*Cg9-ibUqX(3JBey zG?ymDf4|=xtLdw-NT|ftWLbODufjYi@$ z+kmmhtL>w<@Z7_83JLhL;{%c;t1!biVkdg>JFMAB} zP$_abh$x!*(K0N2BTT>3V#6AQiW_fr9P&^&Pb>e_-4#&YH*IbMw9ikP2+6GaziC&{ z?p3cMv3e9H1nHKf!VmB4x^5m3IeyxT0Y?TIByj7K+>L(wun&wMV@(OK8(>;4g{HmVpd z@H?sKvOfFY;6Tt29&5#mf0$PlIqv}}BOVjc?n+ud4RVZ{LISyI`~sfi28)Nye3>(i zb;j%s=Na~9eXVt-MHzOxSF#!&5P-qDhg?*% zf`=uO_s6whMvAiQoA=jua@nwre*MT>! z7`Hx$dv#p4(+f8)1L{-|!5aeJN@L|eDmt;}clM1Fwa&WQrGq?avXq-uw>mGV#@-rW z54`y$@yN-#R_s>gZOv45aTsp7j^YTOGPQP-cD2Cszwo{{EWBB{^yrh|uBCo?4$bVl`qu~? zvq)%@nE3f5C`IC;2!bUf`37QC2(LsXYJ*6Y$dyDKS@)e@%<$W;r;RMD%|Mf&Kd?CD z=Q&nlYhPFibbsIooXJq4Pg4HKyj&zPRRU_i{GBz(E{p`(QR?uNAa_X{=; z?BH$*57D3GHdSq9PTD=-_q(yn+ioqJk+!Nx%Yt^tONNAkL@gMk+g@NBu)dc-hn`|kT!|$$mhK0T5;3TU;fg|*~&Tfh5o z^=1wAz|r38bV~a8sh4(dE0m0OV8?iN=^S60yZLV~ipzy0JF z9}bIgA>Rqr=FcP8%DbZerd!7_(pZ379)Z7EC86XuA_&nlSX}3KuTd+Dzi|NrignSw zsx-2XXF9K-a=Bn%5YJchY{9A;7>l9dW81OO;hv0tO_$-|^mX|sVt42CmEU)WM)Pi+ z3@fzje##;b1|Dl35mc^(a)9#Ok3iTA?fzu}0r=z;L}LEQ{yp}`l{cvC1r^n8Yd0>b zFeUKG+RazsCtK1VHrRJPI9F}&^{ynxqx?zf9Gc9k$vA)7)4V!YPvsplWNy+ULaME# z<7K|`c|An%V3EZyn!9(litH`@Z0BnfZ*b+%cE6w zIbsd*_I6J~KZMTQxgxa{=qjPl&=J@<2nB2yLR4fu$Cw>C{=+l!8d`nR+LS>TVz=wu zaMLd#vRuX=DH;Ti%JbB)NJpStzg{GnbMmQ1Ic`LKEtaXK)!?q%gsA#t6mywfd zZ7GIxsl&Kq1GuJCX|xab~xaRA3gB9VzWX za|qXy;~jvqR>S9uSlvYkOOAyl)VMU+qA^TdOt;E~Yp^BI{vmKe^b2M7;LpdlP-nI) z(9$|YS{=h1-!vIAb`-Sh=ctHk*)CAlpec84ezyy!ixyW31^ENo5-u9YdcxN??Pcx&U`8^TCVPYa4`hNKn@IxSA z(|St#0B(5?Nq5zT)At|By0ky?k-xQs4doDTgibQ`Rp1}DKJZvMiZSty>~GMuo_x=u2_f@&Ggpr!IYbfN ziiEO^GZI5{8!sy_f1_V8X&R*5|MIFxKgMXfW2edob;j^!Wrr>(Ba;)7dr9(EC7DJ4 zSetNV^6}0=HQ(p7Ifq4=218&t29l=w;a(0sn_DLTu9ER{UmSrA!)RG8cAn9y0C>mn zW|&*scyGvkD9g|+Na zzX$xaeP?%9`@!10qR)U=YhO*axSy?gWvrNvEO)TKrKL&2a;2-n<#Y@fd7NZubbMw0I za2_lbsegWfcw<&j{No;Qh8VMeCBHAvte-~j{uvv&pLAi|KjAhXP5LhNOkwd0u2>hH z)pYJYWL7yd(SScNcdaC^0!75DEs8G3-!msFuV|{w zqCKx`<3nM!Kuqh1%Y0+OFitNED^gXx`|m||@L}EDy<2AehjPs%-j$!c!t~@VgSS8R zIlI*Nqe;XkA~Hz$zn8zsW(h*YKVPS4HY)czgH%_kM$#<;MZg69c{*+kTkBkG`@YI@ z!{px;vq=pFW|VI(XWoe2M@s5pRLqH#sZUzWmgz#b2!XOq#bF01$Sfu3M-rHMCD+{4 zF&+l#(y5Iq>~WSQlvj;;CPQ;Zu?BghJg*T*GF`L1HZfVO6I$geuem{K*K=CgXc6tU z3JJ@&$KPo@1k2 z{4vy)i$d4PWe>`Wn=DVl&N*2X3q4P!1m}8gR3Uta*j3DY4Q7}&m1`&FFo_^OSYp}< z&D&vfB9SG(7d`fHvT%2~4=hbrK*-19qHkdFJ0d_NZ9Vv*twEJ+g><-u)acAaYvgmr z58a%>fB+865de~=@Ccu*hcIzu^{KTwm@3zcBB*GUz5jN)3++y-QCWjJ>tcDobF;wt=)xG);`S3IQ+DU#8M3M|5nR zTt8uBrtxx5<8E8!%ei3yot+JWgi~3LEB;RwfCb4iy^bo@iulVkwZ$qmvF_VVrk3|q zlBKnj6rTxac>YYt%-VZJ_vo~_R8k>zDq2X>O~#T&5pu=8 z+sWK%i4mAouKJIe7&iV zih%4EFa@5VfnJu>c*|XP4D3vx4C>r4QfuShcyapE-tOtI{Q24Vb%i|ZkaF}Tx9yS$br^$S}-N^_1N=q=!uYYb%ubMM((xwErsdW?5Bw7`_XhiKv^H+L}?d3pxj#JDI>J&c&+v| zPvn-^!czwe3T>vW_x`M!=4DI4&$ShL99y*D%jP6viS8gD34NYa=>Yf2zR}*nhfo2) zXgT0+k$X4j{mJmWL<}bDgK`k!+_tgIAEs6YG*7Mk-o|PAOO5?(3v&b{Kdv+e8=tkd zM;2)3qCN6Fkkm6)4w2t>*ukNROH@`&DjHu8K=C&DhAtf}jkA`yGm)cAXmRT+8seMi z`*79b^m|!-E%UBD(i8gPDVe0CqKtqVt->CNoFd|Jx2{s}L{D&_Rt4#>2Y{l0Gnjg& zSmC@v?(_9qF?o#~!mDy?QLT4%Dd&k#a*V}D_MFgO~7XXqOs+S#$6NT;C_ z2+E|!zeEeoh%WZdCx9Hn)ho@Tqz$COv^7j?m&mW;?NahOFZM?p2 zIac8Z+A1QBbgB%`rJkgP9z$W3HSu|FuP`bggF3sY-e}k{m$>hdN;-hDuNAOzUX-&p z=aZahWl;i;{vr6Dn>t?3AtLp+U%{HEP=cCz%=tvvuM2-o9*>hU)~l}mwO1vRL_9Xb zff$cA;x{_S(d_3P{WoH;;m-lm(HAQQS4VD-k27bxH@z`*gKi8{J4Du;lKG6)e`k!C zARP)La6B4wmO<25{m>z}j}-AwlRoh^j(0@rZ!{>8_f1~s2O7I^iDw$?7EZyxId6jB zRhx#ca5;Ud|*<48cs!`t!1~HQE6@eZ5ndpD0}25uEQ^GVjZo#? zI=~mgJ`FFN3FmTv@H_sFO!7(&L|AVRdOSA8#Qy=by zUpoHu{bh0k4Wj)WHu#6nFb42{VPY65Ej+ zP{@o@5Ja-2ih*F!-S_vL;K(dH2{743qIm1Ia5R1N(@5>2P;Y!^jH2GB-=pnt`sd>% zA=fEyzia#jTc>2R;O6t4YK&Fi7+>`tJPeu1+4JXcykTn_%_GFD1*<$1tMQm&?2qD; zpb{wbxHm;Py!D9B4UK5u_5tv~`ee+-jngR=&<_bpp#Kt0loh(ha7~ySBJ-xVHq89E z37=ml*uM`_N!iM3Hu$WS?HgOI`sh&#x^&^C8$Y@9oDlx=e;$->i)W;LxG5 zvxn%Y=EZc~UEe4rb!GRP*}P~BYKJ@jt%~g?Iw5Ux&2#=O2wm&t27*U8z+I&O zy2bOA9R{}SuuMcE@iWsZ5@izpY*mHt5ik3qpFP4;6_Pb$mNZ1_BWedOdCc1MVw}Q2 zV!7w0u*ou|kztyzdEK%U>?pRm^@U&)DTmJF$Gglm;SSGu`@zPMHV(YIi(J^Fqc}xs zJz5kl-K7}Knhu|pWh~W6wwFgC9fp3h<#EgQu6GL2?Mw|@kHL1P$_aSj`vV*l_q9j~ zE3ezR?p@~IEClb#W`px!rXl1;lgsV+{`8^XsxHLIG;rdXbg#wFvYqI2R>%pcYkgrl zh3LcOO`RYC>+K;`jr5IOw=zq5)> zI@i{$NY#t!q;>ASIS!YAyGqJ0yQ)tVy&RjB zslOV_bCt7ll`4Ho(JO*JdyZ--iblZCL0h%{K{Sg5@O z=LYtqZhnghLaxxEZ3RuD-NAQk6lXxZ+=LtxzFs%!Tx~%l?j34GYb9}x9fgfV>WRNM zx8HdPd78O5I@?7DC7GOcJH>r4)f}l@bP`4`H-oF#1uRis3zNMY$2%W$`lHOK`u0

NEzKA7bB&3Di`_jy)_{{RYcud5ry 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≻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

NOTE: The total size of fields of the analyzed shards of the index in the response is usually smaller than the index store_size value because some small metadata files are ignored and some parts of data files might not be scanned by the API. Since stored fields are stored together in a compressed format, the sizes of stored fields are also estimates and can be inaccurate. The stored size of the _id field is likely underestimated while the _source field is overestimated.

+

For usage examples see the External documentation or refer to Analyze the index disk usage example for an example.

``_ @@ -3689,11 +3686,7 @@ async def put_data_lifecycle( __body["downsampling"] = downsampling if enabled is not None: __body["enabled"] = enabled - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -3849,11 +3842,7 @@ async def put_data_stream_options( if not __body: if failure_store is not None: __body["failure_store"] = failure_store - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -4677,6 +4666,7 @@ async def refresh( For data streams, the API runs the refresh operation on the stream’s backing indices.

By default, Elasticsearch periodically refreshes indices every second, but only on indices that have received one search request or more in the last 30 seconds. You can change this default interval with the index.refresh_interval setting.

+

In Elastic Cloud Serverless, the default refresh interval is 5 seconds across all indices.

Refresh requests are synchronous and do not return a response until the refresh operation completes.

Refreshes are resource-intensive. To ensure good cluster performance, it's recommended to wait for Elasticsearch's periodic refresh rather than performing an explicit refresh when possible.

@@ -5076,6 +5066,7 @@ async def resolve_index( ] ] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html @@ -5103,6 +5094,10 @@ async def resolve_index( a missing or closed index. :param mode: Filter indices by index mode - standard, lookup, time_series, etc. Comma-separated list of IndexMode. Empty means no filter. + :param project_routing: Specifies a subset of projects to target using project + metadata tags in a subset of Lucene query syntax. Allowed Lucene queries: + the _alias tag and a single value (possibly wildcarded). Examples: _alias:my-project + _alias:_origin _alias:*pr* Supported in serverless only. """ if name in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'name'") @@ -5125,6 +5120,8 @@ async def resolve_index( __query["mode"] = mode if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing __headers = {"accept": "application/json"} return await self.perform_request( # type: ignore[return-value] "GET", @@ -5541,11 +5538,7 @@ async def shrink( __body["aliases"] = aliases if settings is not None: __body["settings"] = settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -5556,7 +5549,9 @@ async def shrink( path_parts=__path_parts, ) - @_rewrite_parameters() + @_rewrite_parameters( + body_name="index_template", + ) async def simulate_index_template( self, *, @@ -5567,6 +5562,8 @@ async def simulate_index_template( filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, include_defaults: t.Optional[bool] = None, + index_template: t.Optional[t.Mapping[str, t.Any]] = None, + body: t.Optional[t.Mapping[str, t.Any]] = None, master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, ) -> ObjectApiResponse[t.Any]: @@ -5586,12 +5583,15 @@ async def simulate_index_template( only be dry-run added if new or can also replace an existing one :param include_defaults: If true, returns all relevant default configurations for the index template. + :param index_template: :param master_timeout: Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. """ if name in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'name'") + if index_template is not None and body is not None: + raise ValueError("Cannot set both 'index_template' and 'body'") __path_parts: t.Dict[str, str] = {"name": _quote(name)} __path = f'/_index_template/_simulate_index/{__path_parts["name"]}' __query: t.Dict[str, t.Any] = {} @@ -5611,12 +5611,18 @@ async def simulate_index_template( __query["master_timeout"] = master_timeout if pretty is not None: __query["pretty"] = pretty + __body = index_template if index_template is not None else body + if not __body: + __body = None __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" return await self.perform_request( # type: ignore[return-value] "POST", __path, params=__query, headers=__headers, + body=__body, endpoint_id="indices.simulate_index_template", path_parts=__path_parts, ) @@ -5884,11 +5890,7 @@ async def split( __body["aliases"] = aliases if settings is not None: __body["settings"] = settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -5965,8 +5967,8 @@ async def stats( are requested). :param include_unloaded_segments: If true, the response includes information from segments that are not loaded into memory. - :param level: Indicates whether statistics are aggregated at the cluster, index, - or shard level. + :param level: Indicates whether statistics are aggregated at the cluster, indices, + or shards level. """ __path_parts: t.Dict[str, str] if index not in SKIP_IN_PATH and metric not in SKIP_IN_PATH: @@ -6132,8 +6134,8 @@ async def validate_query( :param analyze_wildcard: If `true`, wildcard and prefix queries are analyzed. :param analyzer: Analyzer to use for the query string. This parameter can only be used when the `q` query string parameter is specified. - :param default_operator: The default operator for query string query: `AND` or - `OR`. + :param default_operator: The default operator for query string query: `and` or + `or`. :param df: Field to use as default where no field prefix is given in the query string. This parameter can only be used when the `q` query string parameter is specified. diff --git a/elasticsearch/_async/client/inference.py b/elasticsearch/_async/client/inference.py index 16e63938d..934395f17 100644 --- a/elasticsearch/_async/client/inference.py +++ b/elasticsearch/_async/client/inference.py @@ -78,11 +78,7 @@ async def completion( __body["input"] = input if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", __path, @@ -338,11 +334,7 @@ async def inference( __body["query"] = query if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", __path, @@ -529,11 +521,7 @@ async def put_ai21( __body["service"] = service if service_settings is not None: __body["service_settings"] = service_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -627,11 +615,7 @@ async def put_alibabacloud( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -727,11 +711,7 @@ async def put_amazonbedrock( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -833,11 +813,7 @@ async def put_amazonsagemaker( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -930,11 +906,7 @@ async def put_anthropic( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1026,11 +998,7 @@ async def put_azureaistudio( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1130,11 +1098,7 @@ async def put_azureopenai( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1224,11 +1188,7 @@ async def put_cohere( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1239,6 +1199,99 @@ async def put_cohere( path_parts=__path_parts, ) + @_rewrite_parameters( + body_fields=( + "service", + "service_settings", + "chunking_settings", + "task_settings", + ), + ) + async def put_contextualai( + self, + *, + task_type: t.Union[str, t.Literal["rerank"]], + contextualai_inference_id: str, + service: t.Optional[t.Union[str, t.Literal["contextualai"]]] = None, + service_settings: t.Optional[t.Mapping[str, t.Any]] = None, + chunking_settings: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + task_settings: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + body: t.Optional[t.Dict[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + .. raw:: html + +

Create an Contextual AI inference endpoint.

+

Create an inference endpoint to perform an inference task with the contexualai service.

+

To review the available rerank models, refer to https://docs.contextual.ai/api-reference/rerank/rerank#body-model.

+ + + ``_ + + :param task_type: The type of the inference task that the model will perform. + :param contextualai_inference_id: The unique identifier of the inference endpoint. + :param service: The type of service supported for the specified task type. In + this case, `contextualai`. + :param service_settings: Settings used to install the inference model. These + settings are specific to the `contextualai` service. + :param chunking_settings: The chunking configuration object. + :param task_settings: Settings to configure the inference task. These settings + are specific to the task type you specified. + :param timeout: Specifies the amount of time to wait for the inference endpoint + to be created. + """ + if task_type in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'task_type'") + if contextualai_inference_id in SKIP_IN_PATH: + raise ValueError( + "Empty value passed for parameter 'contextualai_inference_id'" + ) + if service is None and body is None: + raise ValueError("Empty value passed for parameter 'service'") + if service_settings is None and body is None: + raise ValueError("Empty value passed for parameter 'service_settings'") + __path_parts: t.Dict[str, str] = { + "task_type": _quote(task_type), + "contextualai_inference_id": _quote(contextualai_inference_id), + } + __path = f'/_inference/{__path_parts["task_type"]}/{__path_parts["contextualai_inference_id"]}' + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = body if body is not None else {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if not __body: + if service is not None: + __body["service"] = service + if service_settings is not None: + __body["service_settings"] = service_settings + if chunking_settings is not None: + __body["chunking_settings"] = chunking_settings + if task_settings is not None: + __body["task_settings"] = task_settings + __headers = {"accept": "application/json", "content-type": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "PUT", + __path, + params=__query, + headers=__headers, + body=__body, + endpoint_id="inference.put_contextualai", + path_parts=__path_parts, + ) + @_rewrite_parameters( body_fields=( "service", @@ -1355,11 +1408,7 @@ async def put_custom( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1439,11 +1488,7 @@ async def put_deepseek( __body["service_settings"] = service_settings if chunking_settings is not None: __body["chunking_settings"] = chunking_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1551,11 +1596,7 @@ async def put_elasticsearch( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1650,11 +1691,7 @@ async def put_elser( __body["service_settings"] = service_settings if chunking_settings is not None: __body["chunking_settings"] = chunking_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1736,11 +1773,7 @@ async def put_googleaistudio( __body["service_settings"] = service_settings if chunking_settings is not None: __body["chunking_settings"] = chunking_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1834,11 +1867,7 @@ async def put_googlevertexai( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1966,11 +1995,7 @@ async def put_hugging_face( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -2062,11 +2087,7 @@ async def put_jinaai( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -2148,11 +2169,7 @@ async def put_llama( __body["service_settings"] = service_settings if chunking_settings is not None: __body["chunking_settings"] = chunking_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -2234,11 +2251,7 @@ async def put_mistral( __body["service_settings"] = service_settings if chunking_settings is not None: __body["chunking_settings"] = chunking_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -2332,11 +2345,7 @@ async def put_openai( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -2427,11 +2436,7 @@ async def put_voyageai( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -2511,11 +2516,7 @@ async def put_watsonx( __body["service"] = service if service_settings is not None: __body["service_settings"] = service_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -2588,11 +2589,7 @@ async def rerank( __body["query"] = query if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", __path, @@ -2656,11 +2653,7 @@ async def sparse_embedding( __body["input"] = input if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", __path, @@ -2672,7 +2665,7 @@ async def sparse_embedding( ) @_rewrite_parameters( - body_fields=("input", "task_settings"), + body_fields=("input", "input_type", "task_settings"), ) async def text_embedding( self, @@ -2682,6 +2675,7 @@ async def text_embedding( error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, + input_type: t.Optional[str] = None, pretty: t.Optional[bool] = None, task_settings: t.Optional[t.Any] = None, timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, @@ -2697,6 +2691,13 @@ async def text_embedding( :param inference_id: The inference Id :param input: Inference input. Either a string or an array of strings. + :param input_type: The input data type for the text embedding model. Possible + values include: * `SEARCH` * `INGEST` * `CLASSIFICATION` * `CLUSTERING` Not + all services support all values. Unsupported values will trigger a validation + exception. Accepted values depend on the configured inference service, refer + to the relevant service-specific documentation for more info. > info > The + `input_type` parameter specified on the root level of the request body will + take precedence over the `input_type` parameter specified in `task_settings`. :param task_settings: Optional task settings :param timeout: Specifies the amount of time to wait for the inference request to complete. @@ -2722,13 +2723,11 @@ async def text_embedding( if not __body: if input is not None: __body["input"] = input + if input_type is not None: + __body["input_type"] = input_type if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", __path, diff --git a/elasticsearch/_async/client/ingest.py b/elasticsearch/_async/client/ingest.py index 3cd0260d5..336169a90 100644 --- a/elasticsearch/_async/client/ingest.py +++ b/elasticsearch/_async/client/ingest.py @@ -580,6 +580,7 @@ async def put_ip_location_database( body_fields=( "deprecated", "description", + "field_access_pattern", "meta", "on_failure", "processors", @@ -594,6 +595,9 @@ async def put_pipeline( deprecated: t.Optional[bool] = None, description: t.Optional[str] = None, error_trace: t.Optional[bool] = None, + field_access_pattern: t.Optional[ + t.Union[str, t.Literal["classic", "flexible"]] + ] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, if_version: t.Optional[int] = None, @@ -621,6 +625,8 @@ async def put_pipeline( or updating a non-deprecated index template, Elasticsearch will emit a deprecation warning. :param description: Description of the ingest pipeline. + :param field_access_pattern: Controls how processors in this pipeline should + read and write data on a document's source. :param if_version: Required version for optimistic concurrency control for pipeline updates :param master_timeout: Period to wait for a connection to the master node. If @@ -667,6 +673,8 @@ async def put_pipeline( __body["deprecated"] = deprecated if description is not None: __body["description"] = description + if field_access_pattern is not None: + __body["field_access_pattern"] = field_access_pattern if meta is not None: __body["_meta"] = meta if on_failure is not None: diff --git a/elasticsearch/_async/client/license.py b/elasticsearch/_async/client/license.py index ba389d0d5..2a5c35189 100644 --- a/elasticsearch/_async/client/license.py +++ b/elasticsearch/_async/client/license.py @@ -104,8 +104,10 @@ async def get( license types. If `false`, this parameter returns platinum for both platinum and enterprise license types. This behavior is maintained for backwards compatibility. This parameter is deprecated and will always be set to true in 8.x. - :param local: Specifies whether to retrieve local information. The default value - is `false`, which means the information is retrieved from the master node. + :param local: Specifies whether to retrieve local information. From 9.2 onwards + the default value is `true`, which means the information is retrieved from + the responding node. In earlier versions the default is `false`, which means + the information is retrieved from the elected master node. """ __path_parts: t.Dict[str, str] = {} __path = "/_license" diff --git a/elasticsearch/_async/client/logstash.py b/elasticsearch/_async/client/logstash.py index c63983710..c724911dc 100644 --- a/elasticsearch/_async/client/logstash.py +++ b/elasticsearch/_async/client/logstash.py @@ -141,7 +141,9 @@ async def put_pipeline( ``_ - :param id: An identifier for the pipeline. + :param id: An identifier for the pipeline. Pipeline IDs must begin with a letter + or underscore and contain only letters, underscores, dashes, hyphens and + numbers. :param pipeline: """ if id in SKIP_IN_PATH: diff --git a/elasticsearch/_async/client/ml.py b/elasticsearch/_async/client/ml.py index fb5af673f..4fba07482 100644 --- a/elasticsearch/_async/client/ml.py +++ b/elasticsearch/_async/client/ml.py @@ -2390,7 +2390,7 @@ async def get_overall_buckets( exclude_interim: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, - overall_score: t.Optional[t.Union[float, str]] = None, + overall_score: t.Optional[float] = None, pretty: t.Optional[bool] = None, start: t.Optional[t.Union[str, t.Any]] = None, top_n: t.Optional[int] = None, @@ -5716,7 +5716,7 @@ async def validate(

Validate an anomaly detection job.

- ``_ + ``_ :param analysis_config: :param analysis_limits: diff --git a/elasticsearch/_async/client/nodes.py b/elasticsearch/_async/client/nodes.py index 1b007e7cb..1ed5cd5eb 100644 --- a/elasticsearch/_async/client/nodes.py +++ b/elasticsearch/_async/client/nodes.py @@ -368,9 +368,7 @@ async def stats( human: t.Optional[bool] = None, include_segment_file_sizes: t.Optional[bool] = None, include_unloaded_segments: t.Optional[bool] = None, - level: t.Optional[ - t.Union[str, t.Literal["cluster", "indices", "shards"]] - ] = None, + level: t.Optional[t.Union[str, t.Literal["indices", "node", "shards"]]] = None, pretty: t.Optional[bool] = None, timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, types: t.Optional[t.Sequence[str]] = None, @@ -404,8 +402,8 @@ async def stats( are requested). :param include_unloaded_segments: If `true`, the response includes information from segments that are not loaded into memory. - :param level: Indicates whether statistics are aggregated at the cluster, index, - or shard level. + :param level: Indicates whether statistics are aggregated at the node, indices, + or shards level. :param timeout: Period to wait for a response. If no response is received before the timeout expires, the request fails and returns an error. :param types: A comma-separated list of document types for the indexing index diff --git a/elasticsearch/_async/client/project.py b/elasticsearch/_async/client/project.py new file mode 100644 index 000000000..6e1e1a63a --- /dev/null +++ b/elasticsearch/_async/client/project.py @@ -0,0 +1,67 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import ( + Stability, + _rewrite_parameters, + _stability_warning, +) + + +class ProjectClient(NamespacedClient): + + @_rewrite_parameters() + @_stability_warning(Stability.EXPERIMENTAL) + async def tags( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + .. raw:: html + +

Return tags defined for the project

+ + """ + __path_parts: t.Dict[str, str] = {} + __path = "/_project/tags" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", + __path, + params=__query, + headers=__headers, + endpoint_id="project.tags", + path_parts=__path_parts, + ) diff --git a/elasticsearch/_async/client/security.py b/elasticsearch/_async/client/security.py index 516906ce8..de094aa03 100644 --- a/elasticsearch/_async/client/security.py +++ b/elasticsearch/_async/client/security.py @@ -2052,6 +2052,45 @@ async def get_settings( path_parts=__path_parts, ) + @_rewrite_parameters() + async def get_stats( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + .. raw:: html + +

Get security stats.

+

Gather security usage statistics from all node(s) within the cluster.

+ + + ``_ + """ + __path_parts: t.Dict[str, str] = {} + __path = "/_security/stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", + __path, + params=__query, + headers=__headers, + endpoint_id="security.get_stats", + path_parts=__path_parts, + ) + @_rewrite_parameters( body_fields=( "grant_type", diff --git a/elasticsearch/_async/client/shutdown.py b/elasticsearch/_async/client/shutdown.py index 5dbc33e92..9502d1fe6 100644 --- a/elasticsearch/_async/client/shutdown.py +++ b/elasticsearch/_async/client/shutdown.py @@ -33,13 +33,9 @@ async def delete_node( error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, - master_timeout: t.Optional[ - t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] - ] = None, + master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, - timeout: t.Optional[ - t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] - ] = None, + timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html @@ -97,9 +93,7 @@ async def get_node( error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, - master_timeout: t.Optional[ - t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] - ] = None, + master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, ) -> ObjectApiResponse[t.Any]: """ @@ -162,14 +156,10 @@ async def put_node( error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, - master_timeout: t.Optional[ - t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] - ] = None, + master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, target_node_name: t.Optional[str] = None, - timeout: t.Optional[ - t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] - ] = None, + timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, body: t.Optional[t.Dict[str, t.Any]] = None, ) -> ObjectApiResponse[t.Any]: """ diff --git a/elasticsearch/_async/client/slm.py b/elasticsearch/_async/client/slm.py index 3eaafd865..88f0d910b 100644 --- a/elasticsearch/_async/client/slm.py +++ b/elasticsearch/_async/client/slm.py @@ -431,11 +431,7 @@ async def put_lifecycle( __body["retention"] = retention if schedule is not None: __body["schedule"] = schedule - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, diff --git a/elasticsearch/_async/client/sql.py b/elasticsearch/_async/client/sql.py index de423ea66..9e146e0f8 100644 --- a/elasticsearch/_async/client/sql.py +++ b/elasticsearch/_async/client/sql.py @@ -285,6 +285,7 @@ async def query( page_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, params: t.Optional[t.Sequence[t.Any]] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, query: t.Optional[str] = None, request_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, @@ -332,6 +333,10 @@ async def query( is no longer available. Subsequent scroll requests prolong the lifetime of the scroll cursor by the duration of `page_timeout` in the scroll request. :param params: The values for parameters in the query. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param query: The SQL query to run. :param request_timeout: The timeout before the request fails. :param runtime_mappings: One or more runtime fields for the search request. These @@ -357,6 +362,8 @@ async def query( __query["human"] = human if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if not __body: if allow_partial_search_results is not None: __body["allow_partial_search_results"] = allow_partial_search_results diff --git a/elasticsearch/_async/client/streams.py b/elasticsearch/_async/client/streams.py new file mode 100644 index 000000000..65e934646 --- /dev/null +++ b/elasticsearch/_async/client/streams.py @@ -0,0 +1,185 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +import typing as t + +from elastic_transport import ObjectApiResponse, TextApiResponse + +from ._base import NamespacedClient +from .utils import ( + Stability, + _rewrite_parameters, + _stability_warning, +) + + +class StreamsClient(NamespacedClient): + + @_rewrite_parameters() + @_stability_warning(Stability.EXPERIMENTAL) + async def logs_disable( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + .. raw:: html + +

Disable logs stream.

+

Turn off the logs stream feature for this cluster.

+ + + ``_ + + :param master_timeout: The period to wait for a connection to the master node. + If no response is received before the timeout expires, the request fails + and returns an error. + :param timeout: The period to wait for a response. If no response is received + before the timeout expires, the request fails and returns an error. + """ + __path_parts: t.Dict[str, str] = {} + __path = "/_streams/logs/_disable" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json,text/plain"} + return await self.perform_request( # type: ignore[return-value] + "POST", + __path, + params=__query, + headers=__headers, + endpoint_id="streams.logs_disable", + path_parts=__path_parts, + ) + + @_rewrite_parameters() + @_stability_warning(Stability.EXPERIMENTAL) + async def logs_enable( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + .. raw:: html + +

Enable logs stream.

+

Turn on the logs stream feature for this cluster.

+

NOTE: To protect existing data, this feature can be turned on only if the + cluster does not have existing indices or data streams that match the pattern logs|logs.*. + If those indices or data streams exist, a 409 - Conflict response and error is returned.

+ + + ``_ + + :param master_timeout: The period to wait for a connection to the master node. + If no response is received before the timeout expires, the request fails + and returns an error. + :param timeout: The period to wait for a response. If no response is received + before the timeout expires, the request fails and returns an error. + """ + __path_parts: t.Dict[str, str] = {} + __path = "/_streams/logs/_enable" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json,text/plain"} + return await self.perform_request( # type: ignore[return-value] + "POST", + __path, + params=__query, + headers=__headers, + endpoint_id="streams.logs_enable", + path_parts=__path_parts, + ) + + @_rewrite_parameters() + @_stability_warning(Stability.EXPERIMENTAL) + async def status( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + .. raw:: html + +

Get the status of streams.

+

Get the current status for all types of streams.

+ + + ``_ + + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + __path_parts: t.Dict[str, str] = {} + __path = "/_streams/status" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", + __path, + params=__query, + headers=__headers, + endpoint_id="streams.status", + path_parts=__path_parts, + ) diff --git a/elasticsearch/_async/client/watcher.py b/elasticsearch/_async/client/watcher.py index e0b7b39ec..8558b9920 100644 --- a/elasticsearch/_async/client/watcher.py +++ b/elasticsearch/_async/client/watcher.py @@ -552,11 +552,7 @@ async def put_watch( __body["transform"] = transform if trigger is not None: __body["trigger"] = trigger - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, diff --git a/elasticsearch/_sync/client/__init__.py b/elasticsearch/_sync/client/__init__.py index 4f97411c1..498f411c1 100644 --- a/elasticsearch/_sync/client/__init__.py +++ b/elasticsearch/_sync/client/__init__.py @@ -63,6 +63,7 @@ from .ml import MlClient from .monitoring import MonitoringClient from .nodes import NodesClient +from .project import ProjectClient from .query_rules import QueryRulesClient from .rollup import RollupClient from .search_application import SearchApplicationClient @@ -74,6 +75,7 @@ from .snapshot import SnapshotClient from .sql import SqlClient from .ssl import SslClient +from .streams import StreamsClient from .synonyms import SynonymsClient from .tasks import TasksClient from .text_structure import TextStructureClient @@ -368,6 +370,7 @@ def __init__( self.migration = MigrationClient(self) self.ml = MlClient(self) self.monitoring = MonitoringClient(self) + self.project = ProjectClient(self) self.query_rules = QueryRulesClient(self) self.rollup = RollupClient(self) self.search_application = SearchApplicationClient(self) @@ -378,6 +381,7 @@ def __init__( self.shutdown = ShutdownClient(self) self.sql = SqlClient(self) self.ssl = SslClient(self) + self.streams = StreamsClient(self) self.synonyms = SynonymsClient(self) self.text_structure = TextStructureClient(self) self.transform = TransformClient(self) @@ -843,11 +847,7 @@ def close_point_in_time( if not __body: if id is not None: __body["id"] = id - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "DELETE", __path, @@ -887,6 +887,7 @@ def count( min_score: t.Optional[float] = None, preference: t.Optional[str] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, q: t.Optional[str] = None, query: t.Optional[t.Mapping[str, t.Any]] = None, routing: t.Optional[str] = None, @@ -920,8 +921,8 @@ def count( This parameter can be used only when the `q` query string parameter is specified. :param analyzer: The analyzer to use for the query string. This parameter can be used only when the `q` query string parameter is specified. - :param default_operator: The default operator for query string query: `AND` or - `OR`. This parameter can be used only when the `q` query string parameter + :param default_operator: The default operator for query string query: `and` or + `or`. This parameter can be used only when the `q` query string parameter is specified. :param df: The field to use as a default when no field prefix is given in the query string. This parameter can be used only when the `q` query string parameter @@ -941,6 +942,10 @@ def count( in the result. :param preference: The node or shard the operation should be performed on. By default, it is random. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param q: The query in Lucene query string syntax. This parameter cannot be used with a request body. :param query: Defines the search query using Query DSL. A request body query @@ -993,6 +998,8 @@ def count( __query["preference"] = preference if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if q is not None: __query["q"] = q if routing is not None: @@ -1042,7 +1049,7 @@ def create( timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, wait_for_active_shards: t.Optional[ t.Union[int, t.Union[str, t.Literal["all", "index-setting"]]] @@ -1221,7 +1228,7 @@ def delete( timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, wait_for_active_shards: t.Optional[ t.Union[int, t.Union[str, t.Literal["all", "index-setting"]]] @@ -1466,8 +1473,8 @@ def delete_by_query( used only when the `q` query string parameter is specified. :param conflicts: What to do if delete by query hits version conflicts: `abort` or `proceed`. - :param default_operator: The default operator for query string query: `AND` or - `OR`. This parameter can be used only when the `q` query string parameter + :param default_operator: The default operator for query string query: `and` or + `or`. This parameter can be used only when the `q` query string parameter is specified. :param df: The field to use as default where no field prefix is given in the query string. This parameter can be used only when the `q` query string parameter @@ -1761,7 +1768,7 @@ def exists( stored_fields: t.Optional[t.Union[str, t.Sequence[str]]] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, ) -> HeadApiResponse: """ @@ -1890,7 +1897,7 @@ def exists_source( source_includes: t.Optional[t.Union[str, t.Sequence[str]]] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, ) -> HeadApiResponse: """ @@ -2019,8 +2026,8 @@ def explain( This parameter can be used only when the `q` query string parameter is specified. :param analyzer: The analyzer to use for the query string. This parameter can be used only when the `q` query string parameter is specified. - :param default_operator: The default operator for query string query: `AND` or - `OR`. This parameter can be used only when the `q` query string parameter + :param default_operator: The default operator for query string query: `and` or + `or`. This parameter can be used only when the `q` query string parameter is specified. :param df: The field to use as default where no field prefix is given in the query string. This parameter can be used only when the `q` query string parameter @@ -2131,6 +2138,7 @@ def field_caps( include_unmapped: t.Optional[bool] = None, index_filter: t.Optional[t.Mapping[str, t.Any]] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, types: t.Optional[t.Sequence[str]] = None, body: t.Optional[t.Dict[str, t.Any]] = None, @@ -2174,6 +2182,11 @@ def field_caps( deleted documents) are outside of the provided range. However, not all queries can rewrite to `match_none` so this API may return an index even if the provided filter matches no document. + :param project_routing: Specifies a subset of projects to target for the field-caps + query using project metadata tags in a subset of Lucene query syntax. Allowed + Lucene queries: the _alias tag and a single value (possibly wildcarded). + Examples: _alias:my-project _alias:_origin _alias:*pr* Supported in serverless + only. :param runtime_mappings: Define ad-hoc runtime fields in the request similar to the way it is done in search requests. These fields exist only as part of the query and take precedence over fields defined with the same name in @@ -2211,6 +2224,8 @@ def field_caps( __query["include_unmapped"] = include_unmapped if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if types is not None: __query["types"] = types if not __body: @@ -2264,7 +2279,7 @@ def get( stored_fields: t.Optional[t.Union[str, t.Sequence[str]]] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, ) -> ObjectApiResponse[t.Any]: """ @@ -2554,7 +2569,7 @@ def get_source( source_includes: t.Optional[t.Union[str, t.Sequence[str]]] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, ) -> ObjectApiResponse[t.Any]: """ @@ -2734,7 +2749,7 @@ def index( timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, wait_for_active_shards: t.Optional[ t.Union[int, t.Union[str, t.Literal["all", "index-setting"]]] @@ -3149,6 +3164,7 @@ def msearch( max_concurrent_shard_requests: t.Optional[int] = None, pre_filter_shard_size: t.Optional[int] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, rest_total_hits_as_int: t.Optional[bool] = None, routing: t.Optional[str] = None, search_type: t.Optional[ @@ -3210,6 +3226,10 @@ def msearch( roundtrip can limit the number of shards significantly if for instance a shard can not match any documents based on its rewrite method i.e., if date filters are mandatory to match but the shard bounds and the query are disjoint. + :param project_routing: Specifies a subset of projects to target for a search + using project metadata tags in a subset Lucene syntax. Allowed Lucene queries: + the _alias tag and a single value (possible wildcarded). Examples: _alias:my-project + _alias:_origin _alias:*pr* Supported in serverless only. :param rest_total_hits_as_int: If true, hits.total are returned as an integer in the response. Defaults to false, which returns an object. :param routing: Custom routing value used to route search operations to a specific @@ -3259,6 +3279,8 @@ def msearch( __query["pre_filter_shard_size"] = pre_filter_shard_size if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if rest_total_hits_as_int is not None: __query["rest_total_hits_as_int"] = rest_total_hits_as_int if routing is not None: @@ -3297,6 +3319,7 @@ def msearch_template( human: t.Optional[bool] = None, max_concurrent_searches: t.Optional[int] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, rest_total_hits_as_int: t.Optional[bool] = None, search_type: t.Optional[ t.Union[str, t.Literal["dfs_query_then_fetch", "query_then_fetch"]] @@ -3330,6 +3353,10 @@ def msearch_template( for cross-cluster search requests. :param max_concurrent_searches: The maximum number of concurrent searches the API can run. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param rest_total_hits_as_int: If `true`, the response returns `hits.total` as an integer. If `false`, it returns `hits.total` as an object. :param search_type: The type of the search operation. @@ -3362,6 +3389,8 @@ def msearch_template( __query["max_concurrent_searches"] = max_concurrent_searches if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if rest_total_hits_as_int is not None: __query["rest_total_hits_as_int"] = rest_total_hits_as_int if search_type is not None: @@ -3407,7 +3436,7 @@ def mtermvectors( term_statistics: t.Optional[bool] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, body: t.Optional[t.Dict[str, t.Any]] = None, ) -> ObjectApiResponse[t.Any]: @@ -3532,6 +3561,7 @@ def open_point_in_time( max_concurrent_shard_requests: t.Optional[int] = None, preference: t.Optional[str] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, routing: t.Optional[str] = None, body: t.Optional[t.Dict[str, t.Any]] = None, ) -> ObjectApiResponse[t.Any]: @@ -3588,6 +3618,11 @@ def open_point_in_time( that each sub-search request executes per node. :param preference: The node or shard the operation should be performed on. By default, it is random. + :param project_routing: Specifies a subset of projects to target for the PIT + request using project metadata tags in a subset of Lucene query syntax. Allowed + Lucene queries: the _alias tag and a single value (possibly wildcarded). + Examples: _alias:my-project _alias:_origin _alias:*pr* Supported in serverless + only. :param routing: A custom value that is used to route operations to a specific shard. """ @@ -3619,6 +3654,8 @@ def open_point_in_time( __query["preference"] = preference if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if routing is not None: __query["routing"] = routing if not __body: @@ -3817,7 +3854,7 @@ def rank_eval( ) @_rewrite_parameters( - body_fields=("dest", "source", "conflicts", "max_docs", "script", "size"), + body_fields=("dest", "source", "conflicts", "max_docs", "script"), ) def reindex( self, @@ -3835,7 +3872,6 @@ def reindex( require_alias: t.Optional[bool] = None, script: t.Optional[t.Mapping[str, t.Any]] = None, scroll: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, - size: t.Optional[int] = None, slices: t.Optional[t.Union[int, t.Union[str, t.Literal["auto"]]]] = None, timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, wait_for_active_shards: t.Optional[ @@ -3911,7 +3947,6 @@ def reindex( reindexing. :param scroll: The period of time that a consistent view of the index should be maintained for scrolled search. - :param size: :param slices: The number of slices this task should be divided into. It defaults to one slice, which means the task isn't sliced into subtasks. Reindex supports sliced scroll to parallelize the reindexing process. This parallelization @@ -3976,8 +4011,6 @@ def reindex( __body["max_docs"] = max_docs if script is not None: __body["script"] = script - if size is not None: - __body["size"] = size __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", @@ -4104,11 +4137,7 @@ def render_search_template( __body["params"] = params if source is not None: __body["source"] = source - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", __path, @@ -4191,11 +4220,7 @@ def scripts_painless_execute( __body["context_setup"] = context_setup if script is not None: __body["script"] = script - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", __path, @@ -4375,6 +4400,7 @@ def search( preference: t.Optional[str] = None, pretty: t.Optional[bool] = None, profile: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, q: t.Optional[str] = None, query: t.Optional[t.Mapping[str, t.Any]] = None, rank: t.Optional[t.Mapping[str, t.Any]] = None, @@ -4472,8 +4498,8 @@ def search( node and the remote clusters are minimized when running cross-cluster search (CCS) requests. :param collapse: Collapses search results the values of the specified field. - :param default_operator: The default operator for the query string query: `AND` - or `OR`. This parameter can be used only when the `q` query string parameter + :param default_operator: The default operator for the query string query: `and` + or `or`. This parameter can be used only when the `q` query string parameter is specified. :param df: The field to use as a default when no field prefix is given in the query string. This parameter can be used only when the `q` query string parameter @@ -4558,6 +4584,10 @@ def search( :param profile: Set to `true` to return detailed timing information about the execution of individual components in a search request. NOTE: This is a debugging tool and adds significant overhead to search execution. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param q: A query in the Lucene query string syntax. Query parameter searches do not support the full Elasticsearch Query DSL but are handy for testing. IMPORTANT: This parameter overrides the query parameter in the request body. @@ -4712,6 +4742,8 @@ def search( __query["preference"] = preference if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if q is not None: __query["q"] = q if request_cache is not None: @@ -4866,6 +4898,7 @@ def search_mvt( ] = None, human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, query: t.Optional[t.Mapping[str, t.Any]] = None, runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, size: t.Optional[int] = None, @@ -5183,6 +5216,10 @@ def search_mvt( In the aggs layer, each feature represents a `geotile_grid` cell. If `grid, each feature is a polygon of the cells bounding box. If `point`, each feature is a Point that is the centroid of the cell. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param query: The query DSL used to filter documents for the search. :param runtime_mappings: Defines one or more runtime fields in the search request. These fields take precedence over mapped fields with the same name. @@ -5246,6 +5283,8 @@ def search_mvt( __query["human"] = human if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if not __body: if aggs is not None: __body["aggs"] = aggs @@ -5419,6 +5458,7 @@ def search_template( preference: t.Optional[str] = None, pretty: t.Optional[bool] = None, profile: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, rest_total_hits_as_int: t.Optional[bool] = None, routing: t.Optional[str] = None, scroll: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, @@ -5464,6 +5504,10 @@ def search_template( :param preference: The node or shard the operation should be performed on. It is random by default. :param profile: If `true`, the query execution is profiled. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param rest_total_hits_as_int: If `true`, `hits.total` is rendered as an integer in the response. If `false`, it is rendered as an object. :param routing: A custom value used to route operations to a specific shard. @@ -5505,6 +5549,8 @@ def search_template( __query["preference"] = preference if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if rest_total_hits_as_int is not None: __query["rest_total_hits_as_int"] = rest_total_hits_as_int if routing is not None: @@ -5631,11 +5677,7 @@ def terms_enum( __body["string"] = string if timeout is not None: __body["timeout"] = timeout - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", __path, @@ -5685,7 +5727,7 @@ def termvectors( term_statistics: t.Optional[bool] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, body: t.Optional[t.Dict[str, t.Any]] = None, ) -> ObjectApiResponse[t.Any]: @@ -6161,8 +6203,8 @@ def update_by_query( be used only when the `q` query string parameter is specified. :param conflicts: The preferred behavior when update by query hits version conflicts: `abort` or `proceed`. - :param default_operator: The default operator for query string query: `AND` or - `OR`. This parameter can be used only when the `q` query string parameter + :param default_operator: The default operator for query string query: `and` or + `or`. This parameter can be used only when the `q` query string parameter is specified. :param df: The field to use as default where no field prefix is given in the query string. This parameter can be used only when the `q` query string parameter diff --git a/elasticsearch/_sync/client/async_search.py b/elasticsearch/_sync/client/async_search.py index 3042ae07a..31e53f89b 100644 --- a/elasticsearch/_sync/client/async_search.py +++ b/elasticsearch/_sync/client/async_search.py @@ -287,6 +287,7 @@ def submit( preference: t.Optional[str] = None, pretty: t.Optional[bool] = None, profile: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, q: t.Optional[str] = None, query: t.Optional[t.Mapping[str, t.Any]] = None, request_cache: t.Optional[bool] = None, @@ -408,6 +409,10 @@ def submit( :param preference: Specify the node or shard the operation should be performed on (default: random) :param profile: + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param q: Query in the Lucene query string syntax :param query: Defines the search definition using the Query DSL. :param request_cache: Specify if request cache should be used for this request @@ -528,6 +533,8 @@ def submit( __query["preference"] = preference if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if q is not None: __query["q"] = q if request_cache is not None: diff --git a/elasticsearch/_sync/client/cat.py b/elasticsearch/_sync/client/cat.py index 5349a32ec..2461c6097 100644 --- a/elasticsearch/_sync/client/cat.py +++ b/elasticsearch/_sync/client/cat.py @@ -36,6 +36,9 @@ def aliases( self, *, name: t.Optional[t.Union[str, t.Sequence[str]]] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, expand_wildcards: t.Optional[ t.Union[ @@ -80,6 +83,9 @@ def aliases( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -95,6 +101,14 @@ def aliases( :param name: A comma-separated list of aliases to retrieve. Supports wildcards (`*`). To retrieve all aliases, omit this parameter or use `*` or `_all`. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param expand_wildcards: The type of index that wildcard patterns can match. If the request can target data streams, this argument determines whether wildcard expressions match hidden data streams. It supports comma-separated @@ -112,6 +126,12 @@ def aliases( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -122,6 +142,8 @@ def aliases( __path_parts = {} __path = "/_cat/aliases" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if expand_wildcards is not None: @@ -142,6 +164,8 @@ def aliases( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -213,6 +237,9 @@ def allocation( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -227,7 +254,14 @@ def allocation( :param node_id: A comma-separated list of node identifiers or names used to limit the returned information. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -242,6 +276,12 @@ def allocation( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -274,6 +314,8 @@ def allocation( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -291,6 +333,9 @@ def component_templates( self, *, name: t.Optional[str] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -330,6 +375,9 @@ def component_templates( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -346,6 +394,14 @@ def component_templates( :param name: The name of the component template. It accepts wildcard expressions. If it is omitted, all component templates are returned. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -360,6 +416,12 @@ def component_templates( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -370,6 +432,8 @@ def component_templates( __path_parts = {} __path = "/_cat/component_templates" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -390,6 +454,8 @@ def component_templates( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -407,6 +473,9 @@ def count( self, *, index: t.Optional[t.Union[str, t.Sequence[str]]] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -419,7 +488,11 @@ def count( help: t.Optional[bool] = None, human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -437,15 +510,33 @@ def count( :param index: A comma-separated list of data streams, indices, and aliases used to limit the request. It supports wildcards (`*`). To target all data streams and indices, omit this parameter or use `*` or `_all`. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple wildcards. :param help: When set to `true` will output available columns. This option can't be combined with any other query string option. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -456,6 +547,8 @@ def count( __path_parts = {} __path = "/_cat/count" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -470,8 +563,12 @@ def count( __query["human"] = human if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -507,6 +604,9 @@ def fielddata( human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -522,7 +622,14 @@ def fielddata( :param fields: Comma-separated list of fields used to limit returned information. To retrieve all fields, omit this parameter. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -532,6 +639,12 @@ def fielddata( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -560,6 +673,8 @@ def fielddata( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -576,6 +691,9 @@ def fielddata( def health( self, *, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -652,6 +770,14 @@ def health( ``_ + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -661,13 +787,20 @@ def health( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param ts: If true, returns `HH:MM:SS` and Unix epoch timestamps. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] = {} __path = "/_cat/health" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -1092,7 +1225,14 @@ def indices( :param index: Comma-separated list of data streams, indices, and aliases used to limit the request. Supports wildcards (`*`). To target all data streams and indices, omit this parameter or use `*` or `_all`. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param expand_wildcards: The type of index that wildcard patterns can match. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. @@ -1109,7 +1249,12 @@ def indices( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -1166,6 +1311,9 @@ def indices( def master( self, *, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -1181,6 +1329,9 @@ def master( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -1193,6 +1344,14 @@ def master( ``_ + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -1207,11 +1366,19 @@ def master( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] = {} __path = "/_cat/master" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -1232,6 +1399,8 @@ def master( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -1374,8 +1543,15 @@ def ml_data_frame_analytics( :param id: The ID of the data frame analytics to fetch :param allow_no_match: Whether to ignore if a wildcard expression matches no - configs. (This includes `_all` string or when no configs have been specified) - :param bytes: The unit in which to display byte values + configs. (This includes `_all` string or when no configs have been specified.) + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: Comma-separated list of column names to display. @@ -1383,7 +1559,12 @@ def ml_data_frame_analytics( be combined with any other query string option. :param s: Comma-separated list of column names or column aliases used to sort the response. - :param time: Unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -1434,6 +1615,9 @@ def ml_datafeeds( *, datafeed_id: t.Optional[str] = None, allow_no_match: t.Optional[bool] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -1549,6 +1733,14 @@ def ml_datafeeds( array when there are no matches and the subset of results when there are partial matches. If `false`, the API returns a 404 status code when there are no matches or only partial matches. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: Comma-separated list of column names to display. @@ -1556,7 +1748,12 @@ def ml_datafeeds( be combined with any other query string option. :param s: Comma-separated list of column names or column aliases used to sort the response. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -1569,6 +1766,8 @@ def ml_datafeeds( __query: t.Dict[str, t.Any] = {} if allow_no_match is not None: __query["allow_no_match"] = allow_no_match + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -1914,7 +2113,14 @@ def ml_jobs( array when there are no matches and the subset of results when there are partial matches. If `false`, the API returns a 404 status code when there are no matches or only partial matches. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: Comma-separated list of column names to display. @@ -1922,7 +2128,12 @@ def ml_jobs( be combined with any other query string option. :param s: Comma-separated list of column names or column aliases used to sort the response. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -2099,7 +2310,14 @@ def ml_trained_models( when there are no matches and the subset of results when there are partial matches. If `false`, the API returns a 404 status code when there are no matches or only partial matches. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param from_: Skips the specified number of transforms. @@ -2109,7 +2327,12 @@ def ml_trained_models( :param s: A comma-separated list of column names or aliases used to sort the response. :param size: The maximum number of transforms to display. - :param time: Unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -2162,6 +2385,9 @@ def ml_trained_models( def nodeattrs( self, *, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -2189,6 +2415,9 @@ def nodeattrs( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -2201,6 +2430,14 @@ def nodeattrs( ``_ + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -2215,11 +2452,19 @@ def nodeattrs( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] = {} __path = "/_cat/nodeattrs" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -2240,6 +2485,8 @@ def nodeattrs( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -2262,7 +2509,7 @@ def nodes( error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, - full_id: t.Optional[t.Union[bool, str]] = None, + full_id: t.Optional[bool] = None, h: t.Optional[ t.Union[ t.Sequence[ @@ -2478,7 +2725,14 @@ def nodes( ``_ - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param full_id: If `true`, return the full node ID. If `false`, return the shortened @@ -2493,7 +2747,12 @@ def nodes( :param s: A comma-separated list of column names or aliases that determines the sort order. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] = {} @@ -2541,6 +2800,9 @@ def nodes( def pending_tasks( self, *, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -2578,6 +2840,14 @@ def pending_tasks( ``_ + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -2592,12 +2862,19 @@ def pending_tasks( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: Unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] = {} __path = "/_cat/pending_tasks" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -2636,6 +2913,9 @@ def pending_tasks( def plugins( self, *, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -2659,6 +2939,9 @@ def plugins( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -2671,6 +2954,14 @@ def plugins( ``_ + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -2686,11 +2977,19 @@ def plugins( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] = {} __path = "/_cat/plugins" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -2713,6 +3012,8 @@ def plugins( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -2831,7 +3132,14 @@ def recovery( to limit the request. Supports wildcards (`*`). To target all data streams and indices, omit this parameter or use `*` or `_all`. :param active_only: If `true`, the response only includes ongoing shard recoveries. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param detailed: If `true`, the response includes detailed information about shard recoveries. :param format: Specifies the format to return the columnar data in, can be set @@ -2843,7 +3151,12 @@ def recovery( :param s: A comma-separated list of column names or aliases that determines the sort order. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -2894,6 +3207,9 @@ def recovery( def repositories( self, *, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -2904,6 +3220,9 @@ def repositories( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -2916,6 +3235,14 @@ def repositories( ``_ + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: List of columns to appear in the response. Supports simple wildcards. @@ -2929,11 +3256,19 @@ def repositories( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] = {} __path = "/_cat/repositories" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -2954,6 +3289,8 @@ def repositories( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -3029,6 +3366,9 @@ def segments( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -3045,7 +3385,14 @@ def segments( :param index: A comma-separated list of data streams, indices, and aliases used to limit the request. Supports wildcards (`*`). To target all data streams and indices, omit this parameter or use `*` or `_all`. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -3060,6 +3407,12 @@ def segments( :param s: A comma-separated list of column names or aliases that determines the sort order. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -3092,6 +3445,8 @@ def segments( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -3295,7 +3650,14 @@ def shards( :param index: A comma-separated list of data streams, indices, and aliases used to limit the request. Supports wildcards (`*`). To target all data streams and indices, omit this parameter or use `*` or `_all`. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: List of columns to appear in the response. Supports simple wildcards. @@ -3305,7 +3667,12 @@ def shards( :param s: A comma-separated list of column names or aliases that determines the sort order. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -3355,6 +3722,9 @@ def snapshots( self, *, repository: t.Optional[t.Union[str, t.Sequence[str]]] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -3425,6 +3795,14 @@ def snapshots( :param repository: A comma-separated list of snapshot repositories used to limit the request. Accepts wildcard expressions. `_all` returns all repositories. If any repository fails during the request, Elasticsearch returns an error. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -3437,7 +3815,12 @@ def snapshots( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: Unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -3448,6 +3831,8 @@ def snapshots( __path_parts = {} __path = "/_cat/snapshots" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -3488,6 +3873,9 @@ def tasks( self, *, actions: t.Optional[t.Sequence[str]] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, detailed: t.Optional[bool] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, @@ -3562,6 +3950,14 @@ def tasks( ``_ :param actions: The task action names, which are used to limit the response. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param detailed: If `true`, the response includes detailed information about shard recoveries. :param format: Specifies the format to return the columnar data in, can be set @@ -3576,7 +3972,12 @@ def tasks( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: Unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param timeout: Period to wait for a response. If no response is received before the timeout expires, the request fails and returns an error. :param v: When set to `true` will enable verbose output. @@ -3588,6 +3989,8 @@ def tasks( __query: t.Dict[str, t.Any] = {} if actions is not None: __query["actions"] = actions + if bytes is not None: + __query["bytes"] = bytes if detailed is not None: __query["detailed"] = detailed if error_trace is not None: @@ -3633,6 +4036,9 @@ def templates( self, *, name: t.Optional[str] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -3660,6 +4066,9 @@ def templates( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -3675,6 +4084,14 @@ def templates( :param name: The name of the template to return. Accepts wildcard expressions. If omitted, all templates are returned. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -3689,6 +4106,12 @@ def templates( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -3699,6 +4122,8 @@ def templates( __path_parts = {} __path = "/_cat/templates" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -3719,6 +4144,8 @@ def templates( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -3736,6 +4163,9 @@ def thread_pool( self, *, thread_pool_patterns: t.Optional[t.Union[str, t.Sequence[str]]] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -3819,6 +4249,14 @@ def thread_pool( :param thread_pool_patterns: A comma-separated list of thread pool names used to limit the request. Accepts wildcard expressions. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: List of columns to appear in the response. Supports simple wildcards. @@ -3832,7 +4270,12 @@ def thread_pool( :param s: A comma-separated list of column names or aliases that determines the sort order. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -3843,6 +4286,8 @@ def thread_pool( __path_parts = {} __path = "/_cat/thread_pool" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -3885,6 +4330,9 @@ def transforms( *, transform_id: t.Optional[str] = None, allow_no_match: t.Optional[bool] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -4084,6 +4532,14 @@ def transforms( array when there are no matches and the subset of results when there are partial matches. If `false`, the request returns a 404 status code when there are no matches or only partial matches. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param from_: Skips the specified number of transforms. @@ -4093,7 +4549,12 @@ def transforms( :param s: Comma-separated list of column names or column aliases used to sort the response. :param size: The maximum number of transforms to obtain. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -4106,6 +4567,8 @@ def transforms( __query: t.Dict[str, t.Any] = {} if allow_no_match is not None: __query["allow_no_match"] = allow_no_match + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: diff --git a/elasticsearch/_sync/client/connector.py b/elasticsearch/_sync/client/connector.py index 58771108a..c28a4c823 100644 --- a/elasticsearch/_sync/client/connector.py +++ b/elasticsearch/_sync/client/connector.py @@ -103,7 +103,7 @@ def delete( :param connector_id: The unique identifier of the connector to be deleted :param delete_sync_jobs: A flag indicating if associated sync jobs should be - also removed. Defaults to false. + also removed. :param hard: A flag indicating if the connector should be hard deleted. """ if connector_id in SKIP_IN_PATH: @@ -360,7 +360,7 @@ def list( :param connector_name: A comma-separated list of connector names to fetch connector documents for - :param from_: Starting offset (default: 0) + :param from_: Starting offset :param include_deleted: A flag to indicate if the desired connector should be fetched, even if it was soft-deleted. :param index_name: A comma-separated list of connector index names to fetch connector @@ -955,7 +955,7 @@ def sync_job_list( ``_ :param connector_id: A connector id to fetch connector sync jobs for - :param from_: Starting offset (default: 0) + :param from_: Starting offset :param job_type: A comma-separated list of job types to fetch the sync jobs for :param size: Specifies a max number of results to get :param status: A sync job status to fetch connector sync jobs for diff --git a/elasticsearch/_sync/client/eql.py b/elasticsearch/_sync/client/eql.py index 1fe0234dd..aa53d77b9 100644 --- a/elasticsearch/_sync/client/eql.py +++ b/elasticsearch/_sync/client/eql.py @@ -229,6 +229,7 @@ def search( keep_on_completion: t.Optional[bool] = None, max_samples_per_key: t.Optional[int] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, result_position: t.Optional[t.Union[str, t.Literal["head", "tail"]]] = None, runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, size: t.Optional[int] = None, @@ -285,6 +286,10 @@ def search( `size` parameter to get a smaller or larger set of samples. To retrieve more than one sample per set of join keys, use the `max_samples_per_key` parameter. Pipes are not supported for sample queries. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param result_position: :param runtime_mappings: :param size: For basic queries, the maximum number of matching events to return. @@ -318,6 +323,8 @@ def search( __query["ignore_unavailable"] = ignore_unavailable if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if not __body: if query is not None: __body["query"] = query diff --git a/elasticsearch/_sync/client/esql.py b/elasticsearch/_sync/client/esql.py index 80843c2d3..b76111b20 100644 --- a/elasticsearch/_sync/client/esql.py +++ b/elasticsearch/_sync/client/esql.py @@ -40,6 +40,7 @@ class EsqlClient(NamespacedClient): "columnar", "filter", "include_ccs_metadata", + "include_execution_metadata", "keep_alive", "keep_on_completion", "locale", @@ -71,6 +72,7 @@ def async_query( ] = None, human: t.Optional[bool] = None, include_ccs_metadata: t.Optional[bool] = None, + include_execution_metadata: t.Optional[bool] = None, keep_alive: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, keep_on_completion: t.Optional[bool] = None, locale: t.Optional[str] = None, @@ -120,7 +122,11 @@ def async_query( be returned if the async query doesn't finish within the timeout. The query ID and running status are available in the `X-Elasticsearch-Async-Id` and `X-Elasticsearch-Async-Is-Running` HTTP headers of the response, respectively. - :param include_ccs_metadata: When set to `true` and performing a cross-cluster + :param include_ccs_metadata: When set to `true` and performing a cross-cluster/cross-project + query, the response will include an extra `_clusters` object with information + about the clusters that participated in the search along with info such as + shards count. + :param include_execution_metadata: When set to `true` and performing a cross-cluster/cross-project query, the response will include an extra `_clusters` object with information about the clusters that participated in the search along with info such as shards count. @@ -180,6 +186,8 @@ def async_query( __body["filter"] = filter if include_ccs_metadata is not None: __body["include_ccs_metadata"] = include_ccs_metadata + if include_execution_metadata is not None: + __body["include_execution_metadata"] = include_execution_metadata if keep_alive is not None: __body["keep_alive"] = keep_alive if keep_on_completion is not None: @@ -486,6 +494,7 @@ def list_queries( "columnar", "filter", "include_ccs_metadata", + "include_execution_metadata", "locale", "params", "profile", @@ -514,8 +523,16 @@ def query( ] = None, human: t.Optional[bool] = None, include_ccs_metadata: t.Optional[bool] = None, + include_execution_metadata: t.Optional[bool] = None, locale: t.Optional[str] = None, - params: t.Optional[t.Sequence[t.Union[None, bool, float, int, str]]] = None, + params: t.Optional[ + t.Sequence[ + t.Union[ + t.Sequence[t.Union[None, bool, float, int, str]], + t.Union[None, bool, float, int, str], + ] + ] + ] = None, pretty: t.Optional[bool] = None, profile: t.Optional[bool] = None, tables: t.Optional[ @@ -554,7 +571,11 @@ def query( :param format: A short version of the Accept header, e.g. json, yaml. `csv`, `tsv`, and `txt` formats will return results in a tabular format, excluding other metadata fields from the response. - :param include_ccs_metadata: When set to `true` and performing a cross-cluster + :param include_ccs_metadata: When set to `true` and performing a cross-cluster/cross-project + query, the response will include an extra `_clusters` object with information + about the clusters that participated in the search along with info such as + shards count. + :param include_execution_metadata: When set to `true` and performing a cross-cluster/cross-project query, the response will include an extra `_clusters` object with information about the clusters that participated in the search along with info such as shards count. @@ -600,6 +621,8 @@ def query( __body["filter"] = filter if include_ccs_metadata is not None: __body["include_ccs_metadata"] = include_ccs_metadata + if include_execution_metadata is not None: + __body["include_execution_metadata"] = include_execution_metadata if locale is not None: __body["locale"] = locale if params is not None: diff --git a/elasticsearch/_sync/client/fleet.py b/elasticsearch/_sync/client/fleet.py index 44178398d..c6fdaa4a4 100644 --- a/elasticsearch/_sync/client/fleet.py +++ b/elasticsearch/_sync/client/fleet.py @@ -642,11 +642,7 @@ def search( __body["track_total_hits"] = track_total_hits if version is not None: __body["version"] = version - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", __path, diff --git a/elasticsearch/_sync/client/graph.py b/elasticsearch/_sync/client/graph.py index 735917b80..7b6543e05 100644 --- a/elasticsearch/_sync/client/graph.py +++ b/elasticsearch/_sync/client/graph.py @@ -97,11 +97,7 @@ def explore( __body["query"] = query if vertices is not None: __body["vertices"] = vertices - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", __path, diff --git a/elasticsearch/_sync/client/ilm.py b/elasticsearch/_sync/client/ilm.py index 26424284a..a61afea52 100644 --- a/elasticsearch/_sync/client/ilm.py +++ b/elasticsearch/_sync/client/ilm.py @@ -383,11 +383,7 @@ def move_to_step( __body["current_step"] = current_step if next_step is not None: __body["next_step"] = next_step - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", __path, @@ -453,11 +449,7 @@ def put_lifecycle( if not __body: if policy is not None: __body["policy"] = policy - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, diff --git a/elasticsearch/_sync/client/indices.py b/elasticsearch/_sync/client/indices.py index 1da7357a4..ffb05d766 100644 --- a/elasticsearch/_sync/client/indices.py +++ b/elasticsearch/_sync/client/indices.py @@ -232,11 +232,7 @@ def analyze( __body["text"] = text if tokenizer is not None: __body["tokenizer"] = tokenizer - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", __path, @@ -812,11 +808,7 @@ def create_from( raise ValueError("Empty value passed for parameter 'source'") if dest in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'dest'") - if create_from is None and body is None: - raise ValueError( - "Empty value passed for parameters 'create_from' and 'body', one of them should be set." - ) - elif create_from is not None and body is not None: + if create_from is not None and body is not None: raise ValueError("Cannot set both 'create_from' and 'body'") __path_parts: t.Dict[str, str] = { "source": _quote(source), @@ -833,7 +825,11 @@ def create_from( if pretty is not None: __query["pretty"] = pretty __body = create_from if create_from is not None else body - __headers = {"accept": "application/json", "content-type": "application/json"} + if not __body: + __body = None + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1393,6 +1389,7 @@ def disk_usage(

NOTE: The total size of fields of the analyzed shards of the index in the response is usually smaller than the index store_size value because some small metadata files are ignored and some parts of data files might not be scanned by the API. Since stored fields are stored together in a compressed format, the sizes of stored fields are also estimates and can be inaccurate. The stored size of the _id field is likely underestimated while the _source field is overestimated.

+

For usage examples see the External documentation or refer to Analyze the index disk usage example for an example.

``_ @@ -3689,11 +3686,7 @@ def put_data_lifecycle( __body["downsampling"] = downsampling if enabled is not None: __body["enabled"] = enabled - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -3849,11 +3842,7 @@ def put_data_stream_options( if not __body: if failure_store is not None: __body["failure_store"] = failure_store - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -4677,6 +4666,7 @@ def refresh( For data streams, the API runs the refresh operation on the stream’s backing indices.

By default, Elasticsearch periodically refreshes indices every second, but only on indices that have received one search request or more in the last 30 seconds. You can change this default interval with the index.refresh_interval setting.

+

In Elastic Cloud Serverless, the default refresh interval is 5 seconds across all indices.

Refresh requests are synchronous and do not return a response until the refresh operation completes.

Refreshes are resource-intensive. To ensure good cluster performance, it's recommended to wait for Elasticsearch's periodic refresh rather than performing an explicit refresh when possible.

@@ -5076,6 +5066,7 @@ def resolve_index( ] ] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html @@ -5103,6 +5094,10 @@ def resolve_index( a missing or closed index. :param mode: Filter indices by index mode - standard, lookup, time_series, etc. Comma-separated list of IndexMode. Empty means no filter. + :param project_routing: Specifies a subset of projects to target using project + metadata tags in a subset of Lucene query syntax. Allowed Lucene queries: + the _alias tag and a single value (possibly wildcarded). Examples: _alias:my-project + _alias:_origin _alias:*pr* Supported in serverless only. """ if name in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'name'") @@ -5125,6 +5120,8 @@ def resolve_index( __query["mode"] = mode if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing __headers = {"accept": "application/json"} return self.perform_request( # type: ignore[return-value] "GET", @@ -5541,11 +5538,7 @@ def shrink( __body["aliases"] = aliases if settings is not None: __body["settings"] = settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -5556,7 +5549,9 @@ def shrink( path_parts=__path_parts, ) - @_rewrite_parameters() + @_rewrite_parameters( + body_name="index_template", + ) def simulate_index_template( self, *, @@ -5567,6 +5562,8 @@ def simulate_index_template( filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, include_defaults: t.Optional[bool] = None, + index_template: t.Optional[t.Mapping[str, t.Any]] = None, + body: t.Optional[t.Mapping[str, t.Any]] = None, master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, ) -> ObjectApiResponse[t.Any]: @@ -5586,12 +5583,15 @@ def simulate_index_template( only be dry-run added if new or can also replace an existing one :param include_defaults: If true, returns all relevant default configurations for the index template. + :param index_template: :param master_timeout: Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. """ if name in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'name'") + if index_template is not None and body is not None: + raise ValueError("Cannot set both 'index_template' and 'body'") __path_parts: t.Dict[str, str] = {"name": _quote(name)} __path = f'/_index_template/_simulate_index/{__path_parts["name"]}' __query: t.Dict[str, t.Any] = {} @@ -5611,12 +5611,18 @@ def simulate_index_template( __query["master_timeout"] = master_timeout if pretty is not None: __query["pretty"] = pretty + __body = index_template if index_template is not None else body + if not __body: + __body = None __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" return self.perform_request( # type: ignore[return-value] "POST", __path, params=__query, headers=__headers, + body=__body, endpoint_id="indices.simulate_index_template", path_parts=__path_parts, ) @@ -5884,11 +5890,7 @@ def split( __body["aliases"] = aliases if settings is not None: __body["settings"] = settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -5965,8 +5967,8 @@ def stats( are requested). :param include_unloaded_segments: If true, the response includes information from segments that are not loaded into memory. - :param level: Indicates whether statistics are aggregated at the cluster, index, - or shard level. + :param level: Indicates whether statistics are aggregated at the cluster, indices, + or shards level. """ __path_parts: t.Dict[str, str] if index not in SKIP_IN_PATH and metric not in SKIP_IN_PATH: @@ -6132,8 +6134,8 @@ def validate_query( :param analyze_wildcard: If `true`, wildcard and prefix queries are analyzed. :param analyzer: Analyzer to use for the query string. This parameter can only be used when the `q` query string parameter is specified. - :param default_operator: The default operator for query string query: `AND` or - `OR`. + :param default_operator: The default operator for query string query: `and` or + `or`. :param df: Field to use as default where no field prefix is given in the query string. This parameter can only be used when the `q` query string parameter is specified. diff --git a/elasticsearch/_sync/client/inference.py b/elasticsearch/_sync/client/inference.py index 7f34eba93..8c906f86c 100644 --- a/elasticsearch/_sync/client/inference.py +++ b/elasticsearch/_sync/client/inference.py @@ -78,11 +78,7 @@ def completion( __body["input"] = input if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", __path, @@ -338,11 +334,7 @@ def inference( __body["query"] = query if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", __path, @@ -529,11 +521,7 @@ def put_ai21( __body["service"] = service if service_settings is not None: __body["service_settings"] = service_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -627,11 +615,7 @@ def put_alibabacloud( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -727,11 +711,7 @@ def put_amazonbedrock( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -833,11 +813,7 @@ def put_amazonsagemaker( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -930,11 +906,7 @@ def put_anthropic( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1026,11 +998,7 @@ def put_azureaistudio( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1130,11 +1098,7 @@ def put_azureopenai( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1224,11 +1188,7 @@ def put_cohere( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1239,6 +1199,99 @@ def put_cohere( path_parts=__path_parts, ) + @_rewrite_parameters( + body_fields=( + "service", + "service_settings", + "chunking_settings", + "task_settings", + ), + ) + def put_contextualai( + self, + *, + task_type: t.Union[str, t.Literal["rerank"]], + contextualai_inference_id: str, + service: t.Optional[t.Union[str, t.Literal["contextualai"]]] = None, + service_settings: t.Optional[t.Mapping[str, t.Any]] = None, + chunking_settings: t.Optional[t.Mapping[str, t.Any]] = None, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + task_settings: t.Optional[t.Mapping[str, t.Any]] = None, + timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + body: t.Optional[t.Dict[str, t.Any]] = None, + ) -> ObjectApiResponse[t.Any]: + """ + .. raw:: html + +

Create an Contextual AI inference endpoint.

+

Create an inference endpoint to perform an inference task with the contexualai service.

+

To review the available rerank models, refer to https://docs.contextual.ai/api-reference/rerank/rerank#body-model.

+ + + ``_ + + :param task_type: The type of the inference task that the model will perform. + :param contextualai_inference_id: The unique identifier of the inference endpoint. + :param service: The type of service supported for the specified task type. In + this case, `contextualai`. + :param service_settings: Settings used to install the inference model. These + settings are specific to the `contextualai` service. + :param chunking_settings: The chunking configuration object. + :param task_settings: Settings to configure the inference task. These settings + are specific to the task type you specified. + :param timeout: Specifies the amount of time to wait for the inference endpoint + to be created. + """ + if task_type in SKIP_IN_PATH: + raise ValueError("Empty value passed for parameter 'task_type'") + if contextualai_inference_id in SKIP_IN_PATH: + raise ValueError( + "Empty value passed for parameter 'contextualai_inference_id'" + ) + if service is None and body is None: + raise ValueError("Empty value passed for parameter 'service'") + if service_settings is None and body is None: + raise ValueError("Empty value passed for parameter 'service_settings'") + __path_parts: t.Dict[str, str] = { + "task_type": _quote(task_type), + "contextualai_inference_id": _quote(contextualai_inference_id), + } + __path = f'/_inference/{__path_parts["task_type"]}/{__path_parts["contextualai_inference_id"]}' + __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = body if body is not None else {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + if not __body: + if service is not None: + __body["service"] = service + if service_settings is not None: + __body["service_settings"] = service_settings + if chunking_settings is not None: + __body["chunking_settings"] = chunking_settings + if task_settings is not None: + __body["task_settings"] = task_settings + __headers = {"accept": "application/json", "content-type": "application/json"} + return self.perform_request( # type: ignore[return-value] + "PUT", + __path, + params=__query, + headers=__headers, + body=__body, + endpoint_id="inference.put_contextualai", + path_parts=__path_parts, + ) + @_rewrite_parameters( body_fields=( "service", @@ -1355,11 +1408,7 @@ def put_custom( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1439,11 +1488,7 @@ def put_deepseek( __body["service_settings"] = service_settings if chunking_settings is not None: __body["chunking_settings"] = chunking_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1551,11 +1596,7 @@ def put_elasticsearch( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1650,11 +1691,7 @@ def put_elser( __body["service_settings"] = service_settings if chunking_settings is not None: __body["chunking_settings"] = chunking_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1736,11 +1773,7 @@ def put_googleaistudio( __body["service_settings"] = service_settings if chunking_settings is not None: __body["chunking_settings"] = chunking_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1834,11 +1867,7 @@ def put_googlevertexai( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1966,11 +1995,7 @@ def put_hugging_face( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -2062,11 +2087,7 @@ def put_jinaai( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -2148,11 +2169,7 @@ def put_llama( __body["service_settings"] = service_settings if chunking_settings is not None: __body["chunking_settings"] = chunking_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -2234,11 +2251,7 @@ def put_mistral( __body["service_settings"] = service_settings if chunking_settings is not None: __body["chunking_settings"] = chunking_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -2332,11 +2345,7 @@ def put_openai( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -2427,11 +2436,7 @@ def put_voyageai( __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -2511,11 +2516,7 @@ def put_watsonx( __body["service"] = service if service_settings is not None: __body["service_settings"] = service_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -2588,11 +2589,7 @@ def rerank( __body["query"] = query if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", __path, @@ -2656,11 +2653,7 @@ def sparse_embedding( __body["input"] = input if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", __path, @@ -2672,7 +2665,7 @@ def sparse_embedding( ) @_rewrite_parameters( - body_fields=("input", "task_settings"), + body_fields=("input", "input_type", "task_settings"), ) def text_embedding( self, @@ -2682,6 +2675,7 @@ def text_embedding( error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, + input_type: t.Optional[str] = None, pretty: t.Optional[bool] = None, task_settings: t.Optional[t.Any] = None, timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, @@ -2697,6 +2691,13 @@ def text_embedding( :param inference_id: The inference Id :param input: Inference input. Either a string or an array of strings. + :param input_type: The input data type for the text embedding model. Possible + values include: * `SEARCH` * `INGEST` * `CLASSIFICATION` * `CLUSTERING` Not + all services support all values. Unsupported values will trigger a validation + exception. Accepted values depend on the configured inference service, refer + to the relevant service-specific documentation for more info. > info > The + `input_type` parameter specified on the root level of the request body will + take precedence over the `input_type` parameter specified in `task_settings`. :param task_settings: Optional task settings :param timeout: Specifies the amount of time to wait for the inference request to complete. @@ -2722,13 +2723,11 @@ def text_embedding( if not __body: if input is not None: __body["input"] = input + if input_type is not None: + __body["input_type"] = input_type if task_settings is not None: __body["task_settings"] = task_settings - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", __path, diff --git a/elasticsearch/_sync/client/ingest.py b/elasticsearch/_sync/client/ingest.py index 5fd38bd78..1286d3627 100644 --- a/elasticsearch/_sync/client/ingest.py +++ b/elasticsearch/_sync/client/ingest.py @@ -580,6 +580,7 @@ def put_ip_location_database( body_fields=( "deprecated", "description", + "field_access_pattern", "meta", "on_failure", "processors", @@ -594,6 +595,9 @@ def put_pipeline( deprecated: t.Optional[bool] = None, description: t.Optional[str] = None, error_trace: t.Optional[bool] = None, + field_access_pattern: t.Optional[ + t.Union[str, t.Literal["classic", "flexible"]] + ] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, if_version: t.Optional[int] = None, @@ -621,6 +625,8 @@ def put_pipeline( or updating a non-deprecated index template, Elasticsearch will emit a deprecation warning. :param description: Description of the ingest pipeline. + :param field_access_pattern: Controls how processors in this pipeline should + read and write data on a document's source. :param if_version: Required version for optimistic concurrency control for pipeline updates :param master_timeout: Period to wait for a connection to the master node. If @@ -667,6 +673,8 @@ def put_pipeline( __body["deprecated"] = deprecated if description is not None: __body["description"] = description + if field_access_pattern is not None: + __body["field_access_pattern"] = field_access_pattern if meta is not None: __body["_meta"] = meta if on_failure is not None: diff --git a/elasticsearch/_sync/client/license.py b/elasticsearch/_sync/client/license.py index aca82098d..d352d114c 100644 --- a/elasticsearch/_sync/client/license.py +++ b/elasticsearch/_sync/client/license.py @@ -104,8 +104,10 @@ def get( license types. If `false`, this parameter returns platinum for both platinum and enterprise license types. This behavior is maintained for backwards compatibility. This parameter is deprecated and will always be set to true in 8.x. - :param local: Specifies whether to retrieve local information. The default value - is `false`, which means the information is retrieved from the master node. + :param local: Specifies whether to retrieve local information. From 9.2 onwards + the default value is `true`, which means the information is retrieved from + the responding node. In earlier versions the default is `false`, which means + the information is retrieved from the elected master node. """ __path_parts: t.Dict[str, str] = {} __path = "/_license" diff --git a/elasticsearch/_sync/client/logstash.py b/elasticsearch/_sync/client/logstash.py index f8abefa14..ae8e2a1dc 100644 --- a/elasticsearch/_sync/client/logstash.py +++ b/elasticsearch/_sync/client/logstash.py @@ -141,7 +141,9 @@ def put_pipeline( ``_ - :param id: An identifier for the pipeline. + :param id: An identifier for the pipeline. Pipeline IDs must begin with a letter + or underscore and contain only letters, underscores, dashes, hyphens and + numbers. :param pipeline: """ if id in SKIP_IN_PATH: diff --git a/elasticsearch/_sync/client/ml.py b/elasticsearch/_sync/client/ml.py index 690197642..08ef976fb 100644 --- a/elasticsearch/_sync/client/ml.py +++ b/elasticsearch/_sync/client/ml.py @@ -2390,7 +2390,7 @@ def get_overall_buckets( exclude_interim: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, - overall_score: t.Optional[t.Union[float, str]] = None, + overall_score: t.Optional[float] = None, pretty: t.Optional[bool] = None, start: t.Optional[t.Union[str, t.Any]] = None, top_n: t.Optional[int] = None, @@ -5716,7 +5716,7 @@ def validate(

Validate an anomaly detection job.

- ``_ + ``_ :param analysis_config: :param analysis_limits: diff --git a/elasticsearch/_sync/client/nodes.py b/elasticsearch/_sync/client/nodes.py index ef6c67b10..9cf656d16 100644 --- a/elasticsearch/_sync/client/nodes.py +++ b/elasticsearch/_sync/client/nodes.py @@ -368,9 +368,7 @@ def stats( human: t.Optional[bool] = None, include_segment_file_sizes: t.Optional[bool] = None, include_unloaded_segments: t.Optional[bool] = None, - level: t.Optional[ - t.Union[str, t.Literal["cluster", "indices", "shards"]] - ] = None, + level: t.Optional[t.Union[str, t.Literal["indices", "node", "shards"]]] = None, pretty: t.Optional[bool] = None, timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, types: t.Optional[t.Sequence[str]] = None, @@ -404,8 +402,8 @@ def stats( are requested). :param include_unloaded_segments: If `true`, the response includes information from segments that are not loaded into memory. - :param level: Indicates whether statistics are aggregated at the cluster, index, - or shard level. + :param level: Indicates whether statistics are aggregated at the node, indices, + or shards level. :param timeout: Period to wait for a response. If no response is received before the timeout expires, the request fails and returns an error. :param types: A comma-separated list of document types for the indexing index diff --git a/elasticsearch/_sync/client/project.py b/elasticsearch/_sync/client/project.py new file mode 100644 index 000000000..57b4e8218 --- /dev/null +++ b/elasticsearch/_sync/client/project.py @@ -0,0 +1,67 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import typing as t + +from elastic_transport import ObjectApiResponse + +from ._base import NamespacedClient +from .utils import ( + Stability, + _rewrite_parameters, + _stability_warning, +) + + +class ProjectClient(NamespacedClient): + + @_rewrite_parameters() + @_stability_warning(Stability.EXPERIMENTAL) + def tags( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + .. raw:: html + +

Return tags defined for the project

+ + """ + __path_parts: t.Dict[str, str] = {} + __path = "/_project/tags" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", + __path, + params=__query, + headers=__headers, + endpoint_id="project.tags", + path_parts=__path_parts, + ) diff --git a/elasticsearch/_sync/client/security.py b/elasticsearch/_sync/client/security.py index 2672a7951..c605f27e3 100644 --- a/elasticsearch/_sync/client/security.py +++ b/elasticsearch/_sync/client/security.py @@ -2052,6 +2052,45 @@ def get_settings( path_parts=__path_parts, ) + @_rewrite_parameters() + def get_stats( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + .. raw:: html + +

Get security stats.

+

Gather security usage statistics from all node(s) within the cluster.

+ + + ``_ + """ + __path_parts: t.Dict[str, str] = {} + __path = "/_security/stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", + __path, + params=__query, + headers=__headers, + endpoint_id="security.get_stats", + path_parts=__path_parts, + ) + @_rewrite_parameters( body_fields=( "grant_type", diff --git a/elasticsearch/_sync/client/shutdown.py b/elasticsearch/_sync/client/shutdown.py index d7ec41511..28b360ca3 100644 --- a/elasticsearch/_sync/client/shutdown.py +++ b/elasticsearch/_sync/client/shutdown.py @@ -33,13 +33,9 @@ def delete_node( error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, - master_timeout: t.Optional[ - t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] - ] = None, + master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, - timeout: t.Optional[ - t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] - ] = None, + timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html @@ -97,9 +93,7 @@ def get_node( error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, - master_timeout: t.Optional[ - t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] - ] = None, + master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, ) -> ObjectApiResponse[t.Any]: """ @@ -162,14 +156,10 @@ def put_node( error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, - master_timeout: t.Optional[ - t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] - ] = None, + master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, target_node_name: t.Optional[str] = None, - timeout: t.Optional[ - t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] - ] = None, + timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, body: t.Optional[t.Dict[str, t.Any]] = None, ) -> ObjectApiResponse[t.Any]: """ diff --git a/elasticsearch/_sync/client/slm.py b/elasticsearch/_sync/client/slm.py index 9b701de80..2541d70f6 100644 --- a/elasticsearch/_sync/client/slm.py +++ b/elasticsearch/_sync/client/slm.py @@ -431,11 +431,7 @@ def put_lifecycle( __body["retention"] = retention if schedule is not None: __body["schedule"] = schedule - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, diff --git a/elasticsearch/_sync/client/sql.py b/elasticsearch/_sync/client/sql.py index b2750ede1..bee0ba437 100644 --- a/elasticsearch/_sync/client/sql.py +++ b/elasticsearch/_sync/client/sql.py @@ -285,6 +285,7 @@ def query( page_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, params: t.Optional[t.Sequence[t.Any]] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, query: t.Optional[str] = None, request_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, @@ -332,6 +333,10 @@ def query( is no longer available. Subsequent scroll requests prolong the lifetime of the scroll cursor by the duration of `page_timeout` in the scroll request. :param params: The values for parameters in the query. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param query: The SQL query to run. :param request_timeout: The timeout before the request fails. :param runtime_mappings: One or more runtime fields for the search request. These @@ -357,6 +362,8 @@ def query( __query["human"] = human if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if not __body: if allow_partial_search_results is not None: __body["allow_partial_search_results"] = allow_partial_search_results diff --git a/elasticsearch/_sync/client/streams.py b/elasticsearch/_sync/client/streams.py new file mode 100644 index 000000000..ba6ff1062 --- /dev/null +++ b/elasticsearch/_sync/client/streams.py @@ -0,0 +1,185 @@ +# Licensed to Elasticsearch B.V. under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch B.V. licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +import typing as t + +from elastic_transport import ObjectApiResponse, TextApiResponse + +from ._base import NamespacedClient +from .utils import ( + Stability, + _rewrite_parameters, + _stability_warning, +) + + +class StreamsClient(NamespacedClient): + + @_rewrite_parameters() + @_stability_warning(Stability.EXPERIMENTAL) + def logs_disable( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + .. raw:: html + +

Disable logs stream.

+

Turn off the logs stream feature for this cluster.

+ + + ``_ + + :param master_timeout: The period to wait for a connection to the master node. + If no response is received before the timeout expires, the request fails + and returns an error. + :param timeout: The period to wait for a response. If no response is received + before the timeout expires, the request fails and returns an error. + """ + __path_parts: t.Dict[str, str] = {} + __path = "/_streams/logs/_disable" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json,text/plain"} + return self.perform_request( # type: ignore[return-value] + "POST", + __path, + params=__query, + headers=__headers, + endpoint_id="streams.logs_disable", + path_parts=__path_parts, + ) + + @_rewrite_parameters() + @_stability_warning(Stability.EXPERIMENTAL) + def logs_enable( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + pretty: t.Optional[bool] = None, + timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: + """ + .. raw:: html + +

Enable logs stream.

+

Turn on the logs stream feature for this cluster.

+

NOTE: To protect existing data, this feature can be turned on only if the + cluster does not have existing indices or data streams that match the pattern logs|logs.*. + If those indices or data streams exist, a 409 - Conflict response and error is returned.

+ + + ``_ + + :param master_timeout: The period to wait for a connection to the master node. + If no response is received before the timeout expires, the request fails + and returns an error. + :param timeout: The period to wait for a response. If no response is received + before the timeout expires, the request fails and returns an error. + """ + __path_parts: t.Dict[str, str] = {} + __path = "/_streams/logs/_enable" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + if timeout is not None: + __query["timeout"] = timeout + __headers = {"accept": "application/json,text/plain"} + return self.perform_request( # type: ignore[return-value] + "POST", + __path, + params=__query, + headers=__headers, + endpoint_id="streams.logs_enable", + path_parts=__path_parts, + ) + + @_rewrite_parameters() + @_stability_warning(Stability.EXPERIMENTAL) + def status( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, + human: t.Optional[bool] = None, + master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + .. raw:: html + +

Get the status of streams.

+

Get the current status for all types of streams.

+ + + ``_ + + :param master_timeout: Period to wait for a connection to the master node. If + no response is received before the timeout expires, the request fails and + returns an error. + """ + __path_parts: t.Dict[str, str] = {} + __path = "/_streams/status" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if master_timeout is not None: + __query["master_timeout"] = master_timeout + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", + __path, + params=__query, + headers=__headers, + endpoint_id="streams.status", + path_parts=__path_parts, + ) diff --git a/elasticsearch/_sync/client/watcher.py b/elasticsearch/_sync/client/watcher.py index 9839cb80b..d14f8481d 100644 --- a/elasticsearch/_sync/client/watcher.py +++ b/elasticsearch/_sync/client/watcher.py @@ -552,11 +552,7 @@ def put_watch( __body["transform"] = transform if trigger is not None: __body["trigger"] = trigger - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", __path, diff --git a/elasticsearch/_version.py b/elasticsearch/_version.py index 8e8baef62..284a52950 100644 --- a/elasticsearch/_version.py +++ b/elasticsearch/_version.py @@ -16,3 +16,4 @@ # under the License. __versionstr__ = "9.1.1" +__es_specification_commit__ = "2f74c26e0a1d66c42232ce2830652c01e8717f00" diff --git a/elasticsearch/client.py b/elasticsearch/client.py index 926ed5fe3..ff511a047 100644 --- a/elasticsearch/client.py +++ b/elasticsearch/client.py @@ -47,6 +47,7 @@ from ._sync.client.ml import MlClient as MlClient # noqa: F401 from ._sync.client.monitoring import MonitoringClient as MonitoringClient # noqa: F401 from ._sync.client.nodes import NodesClient as NodesClient # noqa: F401 +from ._sync.client.project import ProjectClient as ProjectClient # noqa: F401 from ._sync.client.query_rules import QueryRulesClient as QueryRulesClient # noqa: F401 from ._sync.client.rollup import RollupClient as RollupClient # noqa: F401 from ._sync.client.search_application import ( # noqa: F401 @@ -62,6 +63,7 @@ from ._sync.client.snapshot import SnapshotClient as SnapshotClient # noqa: F401 from ._sync.client.sql import SqlClient as SqlClient # noqa: F401 from ._sync.client.ssl import SslClient as SslClient # noqa: F401 +from ._sync.client.streams import StreamsClient as StreamsClient # noqa: F401 from ._sync.client.synonyms import SynonymsClient as SynonymsClient # noqa: F401 from ._sync.client.tasks import TasksClient as TasksClient # noqa: F401 from ._sync.client.text_structure import ( # noqa: F401 @@ -105,6 +107,7 @@ "MlClient", "MonitoringClient", "NodesClient", + "ProjectClient", "RollupClient", "SearchApplicationClient", "SearchableSnapshotsClient", @@ -115,6 +118,7 @@ "SnapshotClient", "SqlClient", "SslClient", + "StreamsClient", "TasksClient", "TextStructureClient", "TransformClient", diff --git a/elasticsearch/dsl/aggs.py b/elasticsearch/dsl/aggs.py index 802d6eca0..2a6b2ff91 100644 --- a/elasticsearch/dsl/aggs.py +++ b/elasticsearch/dsl/aggs.py @@ -653,6 +653,54 @@ def __init__( ) +class CartesianBounds(Agg[_R]): + """ + A metric aggregation that computes the spatial bounding box containing + all values for a Point or Shape field. + + :arg field: The field on which to run the aggregation. + :arg missing: The value to apply to documents that do not have a + value. By default, documents without a value are ignored. + :arg script: + """ + + name = "cartesian_bounds" + + def __init__( + self, + *, + field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, + missing: Union[str, int, float, bool, "DefaultType"] = DEFAULT, + script: Union["types.Script", Dict[str, Any], "DefaultType"] = DEFAULT, + **kwargs: Any, + ): + super().__init__(field=field, missing=missing, script=script, **kwargs) + + +class CartesianCentroid(Agg[_R]): + """ + A metric aggregation that computes the weighted centroid from all + coordinate values for point and shape fields. + + :arg field: The field on which to run the aggregation. + :arg missing: The value to apply to documents that do not have a + value. By default, documents without a value are ignored. + :arg script: + """ + + name = "cartesian_centroid" + + def __init__( + self, + *, + field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, + missing: Union[str, int, float, bool, "DefaultType"] = DEFAULT, + script: Union["types.Script", Dict[str, Any], "DefaultType"] = DEFAULT, + **kwargs: Any, + ): + super().__init__(field=field, missing=missing, script=script, **kwargs) + + class CategorizeText(Bucket[_R]): """ A multi-bucket aggregation that groups semi-structured text into @@ -735,6 +783,43 @@ def __init__( ) +class ChangePoint(Pipeline[_R]): + """ + A sibling pipeline that detects, spikes, dips, and change points in a + metric. Given a distribution of values provided by the sibling multi- + bucket aggregation, this aggregation indicates the bucket of any spike + or dip and/or the bucket at which the largest change in the + distribution of values, if they are statistically significant. There + must be at least 22 bucketed values. Fewer than 1,000 is preferred. + + :arg format: `DecimalFormat` pattern for the output value. If + specified, the formatted value is returned in the aggregation’s + `value_as_string` property. + :arg gap_policy: Policy to apply when gaps are found in the data. + Defaults to `skip` if omitted. + :arg buckets_path: Path to the buckets that contain one set of values + to correlate. + """ + + name = "change_point" + + def __init__( + self, + *, + format: Union[str, "DefaultType"] = DEFAULT, + gap_policy: Union[ + Literal["skip", "insert_zeros", "keep_values"], "DefaultType" + ] = DEFAULT, + buckets_path: Union[ + str, Sequence[str], Mapping[str, str], "DefaultType" + ] = DEFAULT, + **kwargs: Any, + ): + super().__init__( + format=format, gap_policy=gap_policy, buckets_path=buckets_path, **kwargs + ) + + class Children(Bucket[_R]): """ A single bucket aggregation that selects child documents that have the @@ -2980,6 +3065,14 @@ class SignificantTerms(Bucket[_R]): the foreground sample with a term divided by the number of documents in the background with the term. :arg script_heuristic: Customized score, implemented via a script. + :arg p_value: Significant terms heuristic that calculates the p-value + between the term existing in foreground and background sets. The + p-value is the probability of obtaining test results at least as + extreme as the results actually observed, under the assumption + that the null hypothesis is correct. The p-value is calculated + assuming that the foreground set and the background set are + independent https://en.wikipedia.org/wiki/Bernoulli_trial, with + the null hypothesis that the probabilities are the same. :arg shard_min_doc_count: Regulates the certainty a shard has if the term should actually be added to the candidate list or not with respect to the `min_doc_count`. Terms will only be considered if @@ -3033,6 +3126,9 @@ def __init__( script_heuristic: Union[ "types.ScriptedHeuristic", Dict[str, Any], "DefaultType" ] = DEFAULT, + p_value: Union[ + "types.PValueHeuristic", Dict[str, Any], "DefaultType" + ] = DEFAULT, shard_min_doc_count: Union[int, "DefaultType"] = DEFAULT, shard_size: Union[int, "DefaultType"] = DEFAULT, size: Union[int, "DefaultType"] = DEFAULT, @@ -3051,6 +3147,7 @@ def __init__( mutual_information=mutual_information, percentage=percentage, script_heuristic=script_heuristic, + p_value=p_value, shard_min_doc_count=shard_min_doc_count, shard_size=shard_size, size=size, diff --git a/elasticsearch/dsl/field.py b/elasticsearch/dsl/field.py index f7314e441..3b5075287 100644 --- a/elasticsearch/dsl/field.py +++ b/elasticsearch/dsl/field.py @@ -3874,9 +3874,13 @@ class SemanticText(Field): sent in the inference endpoint associated with inference_id. If chunking settings are updated, they will not be applied to existing documents until they are reindexed. + :arg fields: """ name = "semantic_text" + _param_defs = { + "fields": {"type": "field", "hash": True}, + } def __init__( self, @@ -3888,8 +3892,9 @@ def __init__( "types.SemanticTextIndexOptions", Dict[str, Any], "DefaultType" ] = DEFAULT, chunking_settings: Union[ - "types.ChunkingSettings", Dict[str, Any], "DefaultType" + "types.ChunkingSettings", None, Dict[str, Any], "DefaultType" ] = DEFAULT, + fields: Union[Mapping[str, Field], "DefaultType"] = DEFAULT, **kwargs: Any, ): if meta is not DEFAULT: @@ -3902,6 +3907,8 @@ def __init__( kwargs["index_options"] = index_options if chunking_settings is not DEFAULT: kwargs["chunking_settings"] = chunking_settings + if fields is not DEFAULT: + kwargs["fields"] = fields super().__init__(*args, **kwargs) diff --git a/elasticsearch/dsl/query.py b/elasticsearch/dsl/query.py index 0a2cef032..927af6ad4 100644 --- a/elasticsearch/dsl/query.py +++ b/elasticsearch/dsl/query.py @@ -1079,6 +1079,8 @@ class Knn(Query): a query_vector_builder or query_vector, but not both. :arg num_candidates: The number of nearest neighbor candidates to consider per shard + :arg visit_percentage: The percentage of vectors to explore per shard + while doing knn search with bbq_disk :arg k: The final number of nearest neighbors to return as top hits :arg filter: Filters for the kNN search query :arg similarity: The minimum similarity for a vector to be considered @@ -1107,6 +1109,7 @@ def __init__( "types.QueryVectorBuilder", Dict[str, Any], "DefaultType" ] = DEFAULT, num_candidates: Union[int, "DefaultType"] = DEFAULT, + visit_percentage: Union[float, "DefaultType"] = DEFAULT, k: Union[int, "DefaultType"] = DEFAULT, filter: Union[Query, Sequence[Query], "DefaultType"] = DEFAULT, similarity: Union[float, "DefaultType"] = DEFAULT, @@ -1122,6 +1125,7 @@ def __init__( query_vector=query_vector, query_vector_builder=query_vector_builder, num_candidates=num_candidates, + visit_percentage=visit_percentage, k=k, filter=filter, similarity=similarity, @@ -1433,7 +1437,7 @@ def __init__( ] = DEFAULT, version: Union[int, "DefaultType"] = DEFAULT, version_type: Union[ - Literal["internal", "external", "external_gte", "force"], "DefaultType" + Literal["internal", "external", "external_gte"], "DefaultType" ] = DEFAULT, boost: Union[float, "DefaultType"] = DEFAULT, _name: Union[str, "DefaultType"] = DEFAULT, diff --git a/elasticsearch/dsl/response/__init__.py b/elasticsearch/dsl/response/__init__.py index 712cda27b..b58464e16 100644 --- a/elasticsearch/dsl/response/__init__.py +++ b/elasticsearch/dsl/response/__init__.py @@ -233,10 +233,13 @@ def search_after(self) -> "SearchBase[_R]": "types.SimpleValueAggregate", "types.DerivativeAggregate", "types.BucketMetricValueAggregate", + "types.ChangePointAggregate", "types.StatsAggregate", "types.StatsBucketAggregate", "types.ExtendedStatsAggregate", "types.ExtendedStatsBucketAggregate", + "types.CartesianBoundsAggregate", + "types.CartesianCentroidAggregate", "types.GeoBoundsAggregate", "types.GeoCentroidAggregate", "types.HistogramAggregate", diff --git a/elasticsearch/dsl/types.py b/elasticsearch/dsl/types.py index 146ad125b..b62fad025 100644 --- a/elasticsearch/dsl/types.py +++ b/elasticsearch/dsl/types.py @@ -398,14 +398,17 @@ class DenseVectorIndexOptions(AttrDict[Any]): HNSW graph. Only applicable to `hnsw`, `int8_hnsw`, `bbq_hnsw`, and `int4_hnsw` index types. Defaults to `16` if omitted. :arg rescore_vector: The rescore vector options. This is only - applicable to `bbq_hnsw`, `int4_hnsw`, `int8_hnsw`, `bbq_flat`, - `int4_flat`, and `int8_flat` index types. + applicable to `bbq_disk`, `bbq_hnsw`, `int4_hnsw`, `int8_hnsw`, + `bbq_flat`, `int4_flat`, and `int8_flat` index types. + :arg on_disk_rescore: `true` if vector rescoring should be done on- + disk Only applicable to `bbq_hnsw` """ type: Union[ Literal[ "bbq_flat", "bbq_hnsw", + "bbq_disk", "flat", "hnsw", "int4_flat", @@ -421,6 +424,7 @@ class DenseVectorIndexOptions(AttrDict[Any]): rescore_vector: Union[ "DenseVectorIndexOptionsRescoreVector", Dict[str, Any], DefaultType ] + on_disk_rescore: Union[bool, DefaultType] def __init__( self, @@ -429,6 +433,7 @@ def __init__( Literal[ "bbq_flat", "bbq_hnsw", + "bbq_disk", "flat", "hnsw", "int4_flat", @@ -444,6 +449,7 @@ def __init__( rescore_vector: Union[ "DenseVectorIndexOptionsRescoreVector", Dict[str, Any], DefaultType ] = DEFAULT, + on_disk_rescore: Union[bool, DefaultType] = DEFAULT, **kwargs: Any, ): if type is not DEFAULT: @@ -456,6 +462,8 @@ def __init__( kwargs["m"] = m if rescore_vector is not DEFAULT: kwargs["rescore_vector"] = rescore_vector + if on_disk_rescore is not DEFAULT: + kwargs["on_disk_rescore"] = on_disk_rescore super().__init__(kwargs) @@ -2327,9 +2335,7 @@ class LikeDocument(AttrDict[Any]): per_field_analyzer: Union[Mapping[Union[str, InstrumentedField], str], DefaultType] routing: Union[str, DefaultType] version: Union[int, DefaultType] - version_type: Union[ - Literal["internal", "external", "external_gte", "force"], DefaultType - ] + version_type: Union[Literal["internal", "external", "external_gte"], DefaultType] def __init__( self, @@ -2344,7 +2350,7 @@ def __init__( routing: Union[str, DefaultType] = DEFAULT, version: Union[int, DefaultType] = DEFAULT, version_type: Union[ - Literal["internal", "external", "external_gte", "force"], DefaultType + Literal["internal", "external", "external_gte"], DefaultType ] = DEFAULT, **kwargs: Any, ): @@ -2775,6 +2781,31 @@ def __init__( super().__init__(kwargs) +class PValueHeuristic(AttrDict[Any]): + """ + :arg background_is_superset: + :arg normalize_above: Should the results be normalized when above the + given value. Allows for consistent significance results at various + scales. Note: `0` is a special value which means no normalization + """ + + background_is_superset: Union[bool, DefaultType] + normalize_above: Union[int, DefaultType] + + def __init__( + self, + *, + background_is_superset: Union[bool, DefaultType] = DEFAULT, + normalize_above: Union[int, DefaultType] = DEFAULT, + **kwargs: Any, + ): + if background_is_superset is not DEFAULT: + kwargs["background_is_superset"] = background_is_superset + if normalize_above is not DEFAULT: + kwargs["normalize_above"] = normalize_above + super().__init__(kwargs) + + class PercentageScoreHeuristic(AttrDict[Any]): pass @@ -3168,9 +3199,11 @@ def __init__( class SemanticTextIndexOptions(AttrDict[Any]): """ :arg dense_vector: + :arg sparse_vector: """ dense_vector: Union["DenseVectorIndexOptions", Dict[str, Any], DefaultType] + sparse_vector: Union["SparseVectorIndexOptions", Dict[str, Any], DefaultType] def __init__( self, @@ -3178,10 +3211,15 @@ def __init__( dense_vector: Union[ "DenseVectorIndexOptions", Dict[str, Any], DefaultType ] = DEFAULT, + sparse_vector: Union[ + "SparseVectorIndexOptions", Dict[str, Any], DefaultType + ] = DEFAULT, **kwargs: Any, ): if dense_vector is not DEFAULT: kwargs["dense_vector"] = dense_vector + if sparse_vector is not DEFAULT: + kwargs["sparse_vector"] = sparse_vector super().__init__(kwargs) @@ -4030,24 +4068,25 @@ def __init__( class TextEmbedding(AttrDict[Any]): """ - :arg model_id: (required) :arg model_text: (required) + :arg model_id: Model ID is required for all dense_vector fields but + may be inferred for semantic_text fields """ - model_id: Union[str, DefaultType] model_text: Union[str, DefaultType] + model_id: Union[str, DefaultType] def __init__( self, *, - model_id: Union[str, DefaultType] = DEFAULT, model_text: Union[str, DefaultType] = DEFAULT, + model_id: Union[str, DefaultType] = DEFAULT, **kwargs: Any, ): - if model_id is not DEFAULT: - kwargs["model_id"] = model_id if model_text is not DEFAULT: kwargs["model_text"] = model_text + if model_id is not DEFAULT: + kwargs["model_id"] = model_id super().__init__(kwargs) @@ -4680,6 +4719,82 @@ class CardinalityAggregate(AttrDict[Any]): meta: Mapping[str, Any] +class CartesianBoundsAggregate(AttrDict[Any]): + """ + :arg bounds: + :arg meta: + """ + + bounds: "TopLeftBottomRightGeoBounds" + meta: Mapping[str, Any] + + +class CartesianCentroidAggregate(AttrDict[Any]): + """ + :arg count: (required) + :arg location: + :arg meta: + """ + + count: int + location: "CartesianPoint" + meta: Mapping[str, Any] + + +class CartesianPoint(AttrDict[Any]): + """ + :arg x: (required) + :arg y: (required) + """ + + x: float + y: float + + +class ChangePointAggregate(AttrDict[Any]): + """ + :arg type: (required) + :arg bucket: + :arg meta: + """ + + type: "ChangeType" + bucket: "ChangePointBucket" + meta: Mapping[str, Any] + + +class ChangePointBucket(AttrDict[Any]): + """ + :arg key: (required) + :arg doc_count: (required) + """ + + key: Union[int, float, str, bool, None] + doc_count: int + + +class ChangeType(AttrDict[Any]): + """ + :arg dip: + :arg distribution_change: + :arg indeterminable: + :arg non_stationary: + :arg spike: + :arg stationary: + :arg step_change: + :arg trend_change: + """ + + dip: "Dip" + distribution_change: "DistributionChange" + indeterminable: "Indeterminable" + non_stationary: "NonStationary" + spike: "Spike" + stationary: "Stationary" + step_change: "StepChange" + trend_change: "TrendChange" + + class ChildrenAggregate(AttrDict[Any]): """ :arg doc_count: (required) @@ -4957,6 +5072,26 @@ class DfsStatisticsProfile(AttrDict[Any]): children: Sequence["DfsStatisticsProfile"] +class Dip(AttrDict[Any]): + """ + :arg p_value: (required) + :arg change_point: (required) + """ + + p_value: float + change_point: int + + +class DistributionChange(AttrDict[Any]): + """ + :arg p_value: (required) + :arg change_point: (required) + """ + + p_value: float + change_point: int + + class DoubleTermsAggregate(AttrDict[Any]): """ Result of a `terms` aggregation when the field is some kind of decimal @@ -5518,6 +5653,14 @@ class HitsMetadata(AttrDict[Any]): max_score: Union[float, None] +class Indeterminable(AttrDict[Any]): + """ + :arg reason: (required) + """ + + reason: str + + class InferenceAggregate(AttrDict[Any]): """ :arg value: @@ -5920,6 +6063,18 @@ class NestedIdentity(AttrDict[Any]): _nested: "NestedIdentity" +class NonStationary(AttrDict[Any]): + """ + :arg p_value: (required) + :arg r_value: (required) + :arg trend: (required) + """ + + p_value: float + r_value: float + trend: str + + class ParentAggregate(AttrDict[Any]): """ :arg doc_count: (required) @@ -6277,6 +6432,16 @@ class SimpleValueAggregate(AttrDict[Any]): meta: Mapping[str, Any] +class Spike(AttrDict[Any]): + """ + :arg p_value: (required) + :arg change_point: (required) + """ + + p_value: float + change_point: int + + class StandardDeviationBounds(AttrDict[Any]): """ :arg upper: (required) @@ -6313,6 +6478,10 @@ class StandardDeviationBoundsAsString(AttrDict[Any]): lower_sampling: str +class Stationary(AttrDict[Any]): + pass + + class StatsAggregate(AttrDict[Any]): """ Statistics aggregation result. `min`, `max` and `avg` are missing if @@ -6368,6 +6537,16 @@ class StatsBucketAggregate(AttrDict[Any]): meta: Mapping[str, Any] +class StepChange(AttrDict[Any]): + """ + :arg p_value: (required) + :arg change_point: (required) + """ + + p_value: float + change_point: int + + class StringRareTermsAggregate(AttrDict[Any]): """ Result of the `rare_terms` aggregation when the field is a string. @@ -6599,6 +6778,18 @@ class TotalHits(AttrDict[Any]): value: int +class TrendChange(AttrDict[Any]): + """ + :arg p_value: (required) + :arg r_value: (required) + :arg change_point: (required) + """ + + p_value: float + r_value: float + change_point: int + + class UnmappedRareTermsAggregate(AttrDict[Any]): """ Result of a `rare_terms` aggregation when the field is unmapped. diff --git a/pyproject.toml b/pyproject.toml index c12a5a9df..572d90b1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,7 +101,7 @@ Homepage = "https://github.com/elastic/elasticsearch-py" [tool.hatch.version] path = "elasticsearch/_version.py" -pattern = "__versionstr__ = \"(?P[^']+)\"" +pattern = "__versionstr__ = \"(?P[^']+?)\"" [tool.hatch.build.targets.sdist] include = [ From 51551fb640d338cf16ecd89ab359530dc9ae2ad4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 11:31:01 +0000 Subject: [PATCH 14/36] Use an unbuffered queue for the sync bulk helper (#3129) (#3133) (cherry picked from commit 64f11cd6ade65eafe590f78e82c3c8f018028ceb) Co-authored-by: Miguel Grinberg --- elasticsearch/helpers/actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elasticsearch/helpers/actions.py b/elasticsearch/helpers/actions.py index 79197a1e4..22d4ebe22 100644 --- a/elasticsearch/helpers/actions.py +++ b/elasticsearch/helpers/actions.py @@ -261,7 +261,7 @@ def _chunk_actions( yield ret else: item_queue: queue.Queue[_TYPE_BULK_ACTION_HEADER_WITH_META_AND_BODY] = ( - queue.Queue() + queue.Queue(maxsize=1) ) def get_items() -> None: From 03bef1e463d410a267911f361a0d6432e5bf2da7 Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Tue, 28 Oct 2025 16:50:53 +0000 Subject: [PATCH 15/36] Release 9.2.0 (#3134) * Release 9.2.0 * update release notes --- docs/release-notes/index.md | 45 +++++++++++++++++++++++++++++++++++++ elasticsearch/_version.py | 2 +- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/index.md b/docs/release-notes/index.md index 6a5928a15..52124ee90 100644 --- a/docs/release-notes/index.md +++ b/docs/release-notes/index.md @@ -18,6 +18,51 @@ To check for security updates, go to [Security announcements for the Elastic sta % * % ### Fixes [elasticsearch-python-client-next-fixes] +## 9.2.0 (2025-10-28) + +### Enhancements + +* Support Trio when using the `HTTPX `_ async client ([#3089](https://github.com/elastic/elasticsearch-py/pull/3089)) +* Pydantic integration for the DSL module ([#3086](https://github.com/elastic/elasticsearch-py/pull/3086)) +* Add `flush_after_seconds` option to `streaming_bulk()` ([#3064](https://github.com/elastic/elasticsearch-py/pull/3064)) +* Add `TS`, `FUSE` and `INLINE STATS` commands to the ES|QL query builder ([#3096](https://github.com/elastic/elasticsearch-py/pull/3096)) + +### Bug Fixes + +* DSL: support passing inner documents as `AttrDict` instances ([#3080](https://github.com/elastic/elasticsearch-py/pull/3080)) +* DSL: add some recently added field classes as top-level exports for the package ([#3078](https://github.com/elastic/elasticsearch-py/pull/3078)) + +### API + +- Add `streams` namespace with `streams.logs_disable`, `streams.logs_enable`, `streams.status` APIs +- Add `inference.contextualai` API +- Add `security.get_stats` API +- Add `bytes` and `time` parameters to various APIs in the `cat` namespace. +- Add `include_execution_metadata` parameter to `esql.async_query` and `esql.query` APIs +- Add `index_template` parameter to `indices.simulate_index_template` API +- Add `input_type` parameter to `inference.text_embedding` API +- Add `field_access_pattern` parameter to `ingest.put_pipeline` API +- Removed unsupported `size` parameter from `reindex` API + +#### Serverless-specific + +- Add `project` namespace with `project.tags` API +- Add `project_routing` parameter to `count`, `field_caps`, `msearch`, `msearch_template`, `open_point_in_time`, `search`, `search_mvt`, `search_template`, `async_search.submit`, `cat.count`, `eql.search`, `indices.resolve_index`, `sql.query` APIs + +### DSL + +- New `CartesianBounds`, `CartesianCentroid`, `ChangePoint` aggregations +- Add `p_value` parameter to `SignificantTerms` aggregation +- Add `fields` parameter to `SemanticText` field +- Add `visit_percentage` parameter to `Knn` query +- Add `on_disk_rescore` field to `DenseVectorIndexOptions` type +- Add `sparse_vector` field to `SemanticTextIndexOptions` type + +### Other + +* Add 3.14 to CI builds ([#3103](https://github.com/elastic/elasticsearch-py/pull/3103)) +* Drop Python 3.9 support ([#3114](https://github.com/elastic/elasticsearch-py/pull/3114)) + ## 9.1.1 (2025-09-11) diff --git a/elasticsearch/_version.py b/elasticsearch/_version.py index 284a52950..d55ac926d 100644 --- a/elasticsearch/_version.py +++ b/elasticsearch/_version.py @@ -15,5 +15,5 @@ # specific language governing permissions and limitations # under the License. -__versionstr__ = "9.1.1" +__versionstr__ = "9.2.0" __es_specification_commit__ = "2f74c26e0a1d66c42232ce2830652c01e8717f00" From 3480552df9fc1552fb0b726233ab8c2f03886e9b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 14:51:03 +0000 Subject: [PATCH 16/36] Fix new linter error when importing annotationlib (#3143) (#3146) (cherry picked from commit 026f515351a1cb1b36e6cc81e0e10ff5aea569ce) Co-authored-by: Miguel Grinberg --- elasticsearch/dsl/document_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elasticsearch/dsl/document_base.py b/elasticsearch/dsl/document_base.py index d3f7eee09..626179747 100644 --- a/elasticsearch/dsl/document_base.py +++ b/elasticsearch/dsl/document_base.py @@ -39,7 +39,7 @@ try: import annotationlib except ImportError: - annotationlib = None + annotationlib = None # type: ignore[assignment] try: from types import UnionType From 601696fb451841545857d89cc8604f716e420625 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 10:58:04 +0400 Subject: [PATCH 17/36] Fix pytest 9 error (#3152) (#3153) * Fix pytest 9 error * Use explicit pytest_asyncio fixture * Revert "Fix pytest 9 error" This reverts commit 019e7471624a7a335baff11e47e1754e87add3fc. * Fix lint * Fix lint better * Add back type ignore (cherry picked from commit 3a15e1bdc523f0a385398c6c9678fcc30386926e) Co-authored-by: Quentin Pradet --- test_elasticsearch/test_async/test_server/conftest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test_elasticsearch/test_async/test_server/conftest.py b/test_elasticsearch/test_async/test_server/conftest.py index b96811d58..b9a859684 100644 --- a/test_elasticsearch/test_async/test_server/conftest.py +++ b/test_elasticsearch/test_async/test_server/conftest.py @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -import pytest +import pytest_asyncio import sniffio import elasticsearch @@ -23,7 +23,7 @@ from ...utils import CA_CERTS, wipe_cluster -@pytest.fixture(scope="function") +@pytest_asyncio.fixture(scope="function") async def async_client_factory(elasticsearch_url): kwargs = {} if sniffio.current_async_library() == "trio": @@ -42,7 +42,7 @@ async def async_client_factory(elasticsearch_url): await client.close() -@pytest.fixture(scope="function") +@pytest_asyncio.fixture(scope="function") def async_client(async_client_factory): try: yield async_client_factory From a7d5329a4f1beff1eed0044a131010e6ca3b70a6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 15:01:19 +0400 Subject: [PATCH 18/36] Instrument ping with OTel (#3160) (#3164) (cherry picked from commit 3668213600723d6003bf1f3434eee7fdb70ee3f6) Co-authored-by: Quentin Pradet --- elasticsearch/_async/client/__init__.py | 8 +++++++- elasticsearch/_sync/client/__init__.py | 10 +++++++++- noxfile.py | 2 +- test_elasticsearch/test_server/test_otel.py | 11 +++++++++++ test_elasticsearch/utils.py | 1 + 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/elasticsearch/_async/client/__init__.py b/elasticsearch/_async/client/__init__.py index 8c7466a45..15213a6c9 100644 --- a/elasticsearch/_async/client/__init__.py +++ b/elasticsearch/_async/client/__init__.py @@ -504,6 +504,7 @@ async def ping( """ __path = "/" __query: t.Dict[str, t.Any] = {} + __path_parts: t.Dict[str, str] = {} if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -515,7 +516,12 @@ async def ping( __headers = {"accept": "application/json"} try: await self.perform_request( - "HEAD", __path, params=__query, headers=__headers + "HEAD", + __path, + params=__query, + headers=__headers, + endpoint_id="ping", + path_parts=__path_parts, ) return True except (ApiError, TransportError): diff --git a/elasticsearch/_sync/client/__init__.py b/elasticsearch/_sync/client/__init__.py index 498f411c1..bd41b710b 100644 --- a/elasticsearch/_sync/client/__init__.py +++ b/elasticsearch/_sync/client/__init__.py @@ -504,6 +504,7 @@ def ping( """ __path = "/" __query: t.Dict[str, t.Any] = {} + __path_parts: t.Dict[str, str] = {} if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -514,7 +515,14 @@ def ping( __query["pretty"] = pretty __headers = {"accept": "application/json"} try: - self.perform_request("HEAD", __path, params=__query, headers=__headers) + self.perform_request( + "HEAD", + __path, + params=__query, + headers=__headers, + endpoint_id="ping", + path_parts=__path_parts, + ) return True except (ApiError, TransportError): return False diff --git a/noxfile.py b/noxfile.py index ca7f028d2..4bf3d04df 100644 --- a/noxfile.py +++ b/noxfile.py @@ -60,7 +60,7 @@ def test_otel(session): silent=False, ) - argv = pytest_argv() + ["-m", "otel"] + argv = pytest_argv() + ["-m", "otel"] + session.posargs session.run(*argv, env={"TEST_WITH_OTEL": "1"}) diff --git a/test_elasticsearch/test_server/test_otel.py b/test_elasticsearch/test_server/test_otel.py index e0b0cc776..6673e5537 100644 --- a/test_elasticsearch/test_server/test_otel.py +++ b/test_elasticsearch/test_server/test_otel.py @@ -53,6 +53,17 @@ def test_otel_end_to_end(sync_client): assert expected_attributes.items() <= spans[0].attributes.items() +# Since ping is manually implemented, we have a dedicated test for it +def test_otel_ping(sync_client): + tracer, memory_exporter = setup_tracing() + sync_client._otel.tracer = tracer + + sync_client.ping() + spans = memory_exporter.get_finished_spans() + assert len(spans) == 1 + assert spans[0].name == "ping" + + @pytest.mark.parametrize( "bulk_helper_name", ["bulk", "streaming_bulk", "parallel_bulk"] ) diff --git a/test_elasticsearch/utils.py b/test_elasticsearch/utils.py index cfcb5259c..4455462d2 100644 --- a/test_elasticsearch/utils.py +++ b/test_elasticsearch/utils.py @@ -191,3 +191,4 @@ def wait_for_cluster_state_updates_to_finish(client, timeout=30): while time.time() < end_time: if not client.cluster.pending_tasks().get("tasks", ()): break + time.sleep(0.1) From 1f836a5b39e6c6de48b0bd73e83b87ae6ace3ee0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 10:37:14 +0000 Subject: [PATCH 19/36] Update 9.2 API and DSL release notes (#3167) (#3170) (cherry picked from commit 297d70d6f3fce46c7eb887c227f660336c189bd2) Co-authored-by: Miguel Grinberg --- docs/release-notes/index.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/index.md b/docs/release-notes/index.md index 52124ee90..1f90501f8 100644 --- a/docs/release-notes/index.md +++ b/docs/release-notes/index.md @@ -34,6 +34,26 @@ To check for security updates, go to [Security announcements for the Elastic sta ### API +- Add `source_exclude_vectors` to Field Caps, Scroll and Search APIs +- Add `require_data_stream` to Index API +- Add `settings_filter` to Cluster Get Component Template API +- Add `cause` to Cluster Put Component Template API +- Add `master_timeout` to Cluster State API +- Add `ccs_minimize_roundtrips` to EQL Search API +- Add `keep_alive` and `keep_on_completion` to ES|QL Async Query API +- Add `format` to ES|QL Async Query Get API +- Add ES|QL Get Query and List Queries APIs +- Add Indices Delete Data Stream Options API +- Add Indices Get Data Stream Mappings and Put Data Stream Mappings APIs +- Add Indices Get Data Stream Options and Put Data Stream Options APIS +- Add Indices Get Data Stream Settings and Put Data Stream Settings APIs +- Add `allow_no_indices`, `expand_wildcards` and `ignore_available` to Indices Recovery API +- Add Indices Remove Block API +- Add `input_type` to Inference API +- Add `timeout` to all Inference Put APIs +- Add Inference Put Custom API +- Add Inference Put DeepSeek API +- Add `task_settings` to Put HuggingFace API - Add `streams` namespace with `streams.logs_disable`, `streams.logs_enable`, `streams.status` APIs - Add `inference.contextualai` API - Add `security.get_stats` API @@ -42,7 +62,15 @@ To check for security updates, go to [Security announcements for the Elastic sta - Add `index_template` parameter to `indices.simulate_index_template` API - Add `input_type` parameter to `inference.text_embedding` API - Add `field_access_pattern` parameter to `ingest.put_pipeline` API -- Removed unsupported `size` parameter from `reindex` API +- Add `refresh` to Security Grant API Key API +- Add `wait_for_completion` to the Snapshot Delete API +- Add `state` to Snapshot Get API +- Add `refresh` to Synonyms Put Synonym, Put Synonym Rule and Delete Synonym Rule APIs +- Remove unsupported `size` parameter from `reindex` API +- Remove deprecated `if_primary_term`, `if_seq_no` and `op_type` from Create API +- Remove deprecated `master_timeout` from Ingest Get Ip Location Database API +- Remove deprecated `application`, `priviledge` and `username` from the Security Get User API +- Rename `type_query_string` to `type` in License Post Start Trial API #### Serverless-specific @@ -57,6 +85,10 @@ To check for security updates, go to [Security announcements for the Elastic sta - Add `visit_percentage` parameter to `Knn` query - Add `on_disk_rescore` field to `DenseVectorIndexOptions` type - Add `sparse_vector` field to `SemanticTextIndexOptions` type +- Add `index_options` to SparseVector type +- Add `separator_group` and `separators` to ChunkingSettings type +- Add SparseVectorIndexOptions type +- Add key to FiltersBucket type ### Other From 9a7512d5e3f1d9abb5a94b961f178298ca13cc6c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 10:44:20 +0000 Subject: [PATCH 20/36] [DOCS] Clarify upgrades and edit generally (#3138) (#3157) * Revise upgrade section; do a general edit * fix mis-paste * More edits * Review comments + more cleanup (cherry picked from commit 06f25a0f370c21e511b75e02a87d3e7056e2a00e) Co-authored-by: Marci W <333176+marciw@users.noreply.github.com> --- docs/reference/index.md | 57 ++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/docs/reference/index.md b/docs/reference/index.md index 03143bbb2..afc7064a3 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -1,17 +1,18 @@ --- +navigation_title: "Python" mapped_pages: - https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/index.html - https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/overview.html --- -# Python [overview] +# {{es}} Python client [overview] -This is the official Python client for {{es}}. Its goal is to provide common ground for all {{es}}-related code in Python. For this reason, the client is designed to be unopinionated and extendable. API reference documentation for this client is available on [Read the Docs](https://elasticsearch-py.readthedocs.io). +This documentation covers the [official Python client for {{es}}](https://github.com/elastic/elasticsearch-py). The goal of the Python client is to provide common ground for all {{es}}-related code in Python. The client is designed to be unopinionated and extendable. +API reference documentation is provided on [Read the Docs](https://elasticsearch-py.readthedocs.io). -## Example use [_example_use] -Simple use-case: +The following example shows a simple Python client use case: ```python >>> from datetime import datetime @@ -29,14 +30,12 @@ Simple use-case: {'any': 'data', 'timestamp': '2013-05-12T19:45:31.804229'} ``` -::::{tip} -For an elaborate example of how to ingest data into Elastic Cloud, refer to [this page](docs-content://manage-data/ingest/ingesting-data-from-applications/ingest-data-with-python-on-elasticsearch-service.md). -:::: + ## Features [_features] -The client’s features include: +The client's features include: * Translating basic Python data types to and from JSON * Configurable automatic discovery of cluster nodes @@ -46,37 +45,41 @@ The client’s features include: * Thread safety * Pluggable architecture -The client also contains a convenient set of [helpers](client-helpers.md) for some of the more engaging tasks like bulk indexing and reindexing. +The client also provides a convenient set of [helpers](client-helpers.md) for tasks like bulk indexing and reindexing. +::::{tip} +To get started, try this walkthrough: [Ingest data with Python](docs-content://manage-data/ingest/ingesting-data-from-applications/ingest-data-with-python-on-elasticsearch-service.md) +:::: -## Elasticsearch Python DSL [_elasticsearch_python_dsl] - -For a higher level access with more limited scope, have a look at the DSL module, which provides a more convenient and idiomatic way to write and manipulate queries. +### Elasticsearch Python DSL [_elasticsearch_python_dsl] +The [Python DSL module](../reference/elasticsearch-dsl.md) offers a convenient and idiomatic way to write and manipulate queries. -## Compatibility [_compatibility] +## {{es}} version compatibility [_compatibility] -Language clients are _forward compatible:_ each client version works with equivalent and later minor versions of {{es}} without breaking. +Language clients are **forward compatible**: each client version works with equivalent and later minor versions of the **same or next major** version of {{es}}. For full compatibility, the client and {{es}} minor versions should match. -Compatibility does not imply full feature parity. New {{es}} features are supported only in equivalent client versions. For example, an 8.12 client fully supports {{es}} 8.12 features and works with 8.13 without breaking; however, it does not support new {{es}} 8.13 features. An 8.13 client fully supports {{es}} 8.13 features. +| Client version | {{es}} `8.x` | {{es}} `9.x` | {{es}} `10.x` | +|----------------|---------------------------------|---------------------------------|----------------------------------| +| 9.x client | ❌ Not compatible with {{es}} 8.x | ✅ Compatible with {{es}} 9.x | ✅ Compatible with {{es}} 10.x | +| 8.x client | ✅ Compatible with {{es}} 8.x | ✅ Compatible with {{es}} 9.x | ❌ Not compatible with {{es}} 10.x | -| Elasticsearch version | elasticsearch-py branch | -| --- | --- | -| main | main | -| 9.x | 9.x | -| 9.x | 8.x | -| 8.x | 8.x | +Compatibility does not imply feature parity. New {{es}} features are supported only in equivalent client versions. For example, an 8.12 client fully supports {{es}} 8.12 features and works with 8.13 without breaking, but it does not support new {{es}} 8.13 features. An 8.13 client fully supports {{es}} 8.13 features. -{{es}} language clients are also _backward compatible_ across minor versions — with default distributions and without guarantees. +{{es}} language clients are also **backward compatible** across minor versions, with default distributions and without guarantees. ### Major version upgrades -:::{tip} -To upgrade to a new major version, first upgrade {{es}}, then upgrade the Python {{es}} client. +:::{important} +To upgrade to a new major version, first [upgrade {{es}}](docs-content://deploy-manage/upgrade.md), then upgrade the Python client. ::: -Since version 8.0, the {{es}} server supports a compatibility mode that allows smoother upgrade experiences. In a nutshell, this makes it possible to upgrade the {{es}} server to the next major version, while continuing to use the same client. This gives more room to coordinate the upgrade of your codebase to the next major version. +As of version 8.0, {{es}} offers a [compatibility mode](elasticsearch://reference/elasticsearch/rest-apis/compatibility.md) for smoother upgrades. In compatibility mode, you can upgrade your {{es}} cluster to the next major version while continuing to use your existing client during the transition. -For example, to upgrade a system that uses {{es}} 8.x you can upgrade the {{es}} server to 9.x first, and the 8.x Python {{es}} client will continue to work (aside from any breaking changes, which should be listed in the server release notes). You can continue using the 8.x client during the server migration, and only upgrade it once the server migration is complete. The process is described in detail in the [REST API compatibility workflow](https://www.elastic.co/docs/reference/elasticsearch/rest-apis/compatibility#_rest_api_compatibility_workflow) section of the {{es}} documentation. +For example, if you're upgrading {{es}} from 8.x to 9.x, you can continue to use the 8.x Python client during and after the {{es}} server upgrade, with the exception of [breaking changes](../release-notes/breaking-changes.md). -If you need to work with multiple client versions, note that older versions are also released with the `elasticsearch8` and `elasticsearch9` package names so that they can be installed together. +In the Python client, compatibility mode is always enabled. + +:::{tip} +To support working with multiple client versions, the Python client is also released under the package names `elasticsearch8` and `elasticsearch9` (to prevent name collisions). +::: \ No newline at end of file From 3e2d322e809bf6ff5082d382d001e4ef0f8476c6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 10:49:21 +0000 Subject: [PATCH 21/36] Expand the overview section of the docs (#3158) (#3171) (cherry picked from commit 214a6279fb24688269c97249d5a95fd395814e6a) Co-authored-by: Miguel Grinberg --- docs/reference/index.md | 99 +++++++++++++++++++++++++++++++---------- 1 file changed, 76 insertions(+), 23 deletions(-) diff --git a/docs/reference/index.md b/docs/reference/index.md index afc7064a3..2a709230a 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -9,31 +9,75 @@ mapped_pages: This documentation covers the [official Python client for {{es}}](https://github.com/elastic/elasticsearch-py). The goal of the Python client is to provide common ground for all {{es}}-related code in Python. The client is designed to be unopinionated and extendable. -API reference documentation is provided on [Read the Docs](https://elasticsearch-py.readthedocs.io). +## Example [_example] +::::{tab-set} -The following example shows a simple Python client use case: +:::{tab-item} Standard Python +```python +import os +from elasticsearch import Elasticsearch + +def main(): + client = Elasticsearch( + hosts=[os.getenv("ELASTICSEARCH_URL")], + api_key=os.getenv("ELASTIC_API_KEY"), + ) + + resp = client.search( + index="my-index-000001", + from_="40", + size="20", + query={ + "term": { + "user.id": "kimchy" + } + }, + ) + print(resp) +``` +::: +:::{tab-item} Async Python ```python ->>> from datetime import datetime ->>> from elasticsearch import Elasticsearch +import os +from elasticsearch import AsyncElasticsearch + +async def main(): + client = AsyncElasticsearch( + hosts=[os.getenv("ELASTICSEARCH_URL")], + api_key=os.getenv("ELASTIC_API_KEY"), + ) + + resp = await client.search( + index="my-index-000001", + from_="40", + size="20", + query={ + "term": { + "user.id": "kimchy" + } + }, + ) + print(resp) +``` +::: -# Connect to 'http://localhost:9200/' ->>> client = Elasticsearch("http://localhost:9200/") +:::: -# Datetimes will be serialized: ->>> client.index(index="my-index-000001", id=42, document={"any": "data", "timestamp": datetime.now()}) -{'_id': '42', '_index': 'my-index-000001', '_type': 'test-type', '_version': 1, 'ok': True} +## Overview [_overview] -# ...but not deserialized ->>> client.get(index="my-index-000001", id=42)['_source'] -{'any': 'data', 'timestamp': '2013-05-12T19:45:31.804229'} -``` +This package is composed of several modules: +### The actual client +This module, sometimes also called the "low-level" client, implements the support for sending requests to {{es}} servers. The client provides access to the entire surface of the {{es}} API. +* [Getting Started guide](getting-started.md) +* [Ingest data with Python walkthrough](docs-content://manage-data/ingest/ingesting-data-from-applications/ingest-data-with-python-on-elasticsearch-service.md) +* [Reference documentation](https://elasticsearch-py.readthedocs.io/en/stable/es_api.html) -## Features [_features] +#### Features [_features] The client's features include: @@ -45,19 +89,28 @@ The client's features include: * Thread safety * Pluggable architecture -The client also provides a convenient set of [helpers](client-helpers.md) for tasks like bulk indexing and reindexing. +### Bulk helpers -::::{tip} -To get started, try this walkthrough: [Ingest data with Python](docs-content://manage-data/ingest/ingesting-data-from-applications/ingest-data-with-python-on-elasticsearch-service.md) -:::: +The bulk helpers are a set of functions that simplify the ingest of large amounts of data through a high-level interface based on Python iterables. + +* [User guide](client-helpers.md#bulk-helpers) +* [Reference documentation](https://elasticsearch-py.readthedocs.io/en/stable/api_helpers.html) + +### ES|QL query builder -### Elasticsearch Python DSL [_elasticsearch_python_dsl] +This module offers an idiomatic interface to construct ES|QL queries using Python expressions. -The [Python DSL module](../reference/elasticsearch-dsl.md) offers a convenient and idiomatic way to write and manipulate queries. +* [User guide](esql-query-builder.md) +* [Reference documentation](https://elasticsearch-py.readthedocs.io/en/stable/esql.html) -## {{es}} version compatibility [_compatibility] +### DSL module -Language clients are **forward compatible**: each client version works with equivalent and later minor versions of the **same or next major** version of {{es}}. For full compatibility, the client and {{es}} minor versions should match. +The DSL module could be thought of as a "high-level" client for {{es}}. It allows applications to manipulate documents and queries using Python classes and objects instead of primitive types such as dictionaries and lists. + +* [User guide](elasticsearch-dsl.md) +* [Reference documentation](https://elasticsearch-py.readthedocs.io/en/stable/dsl.html) + +## Compatibility [_compatibility] | Client version | {{es}} `8.x` | {{es}} `9.x` | {{es}} `10.x` | |----------------|---------------------------------|---------------------------------|----------------------------------| @@ -82,4 +135,4 @@ In the Python client, compatibility mode is always enabled. :::{tip} To support working with multiple client versions, the Python client is also released under the package names `elasticsearch8` and `elasticsearch9` (to prevent name collisions). -::: \ No newline at end of file +::: From 8fe29ae7d538703b7c81e12f075a496e110fb352 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 11:34:47 +0000 Subject: [PATCH 22/36] load yaml tests from same branch as client (#3169) (#3175) * load yaml tests from same branch as client * restore checks for failed or skipped tests (cherry picked from commit b1ad37aa291e395f37029bd861f1fb335fac1419) Co-authored-by: Miguel Grinberg --- .buildkite/run-repository.sh | 2 ++ .buildkite/run-tests | 1 + .../test_server/test_rest_api_spec.py | 31 ++++++++++++++++--- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/.buildkite/run-repository.sh b/.buildkite/run-repository.sh index ce9344e8d..04d4440f0 100755 --- a/.buildkite/run-repository.sh +++ b/.buildkite/run-repository.sh @@ -21,6 +21,7 @@ echo -e "\033[34;1mINFO:\033[0m TEST_SUITE ${TEST_SUITE}\033[0m" echo -e "\033[34;1mINFO:\033[0m NOX_SESSION ${NOX_SESSION}\033[0m" echo -e "\033[34;1mINFO:\033[0m PYTHON_VERSION ${PYTHON_VERSION}\033[0m" echo -e "\033[34;1mINFO:\033[0m PYTHON_CONNECTION_CLASS ${PYTHON_CONNECTION_CLASS}\033[0m" +echo -e "\033[34;1mINFO:\033[0m ES_YAML_TESTS_BRANCH ${ES_YAML_TESTS_BRANCH}\033[0m" echo -e "\033[1m>>>>> Build [elastic/elasticsearch-py container] >>>>>>>>>>>>>>>>>>>>>>>>>>>>>\033[0m" @@ -42,6 +43,7 @@ docker run \ --env "ELASTICSEARCH_URL=${elasticsearch_url}" \ --env "TEST_SUITE=${TEST_SUITE}" \ --env "PYTHON_CONNECTION_CLASS=${PYTHON_CONNECTION_CLASS}" \ + --env "ES_YAML_TESTS_BRANCH=${ES_YAML_TESTS_BRANCH}" \ --env "TEST_TYPE=server" \ --env "FORCE_COLOR=1" \ --name elasticsearch-py \ diff --git a/.buildkite/run-tests b/.buildkite/run-tests index 8d0eb7ffd..2f37ec9f0 100755 --- a/.buildkite/run-tests +++ b/.buildkite/run-tests @@ -9,6 +9,7 @@ export STACK_VERSION="${STACK_VERSION:=8.0.0-SNAPSHOT}" export TEST_SUITE="${TEST_SUITE:=platinum}" export PYTHON_VERSION="${PYTHON_VERSION:=3.14}" export PYTHON_CONNECTION_CLASS="${PYTHON_CONNECTION_CLASS:=urllib3}" +export ES_YAML_TESTS_BRANCH=${BUILDKITE_PULL_REQUEST_BASE_BRANCH:=main} script_path=$(dirname $(realpath $0)) source $script_path/functions/imports.sh diff --git a/test_elasticsearch/test_server/test_rest_api_spec.py b/test_elasticsearch/test_server/test_rest_api_spec.py index 4e3ff74a9..6db2b1125 100644 --- a/test_elasticsearch/test_server/test_rest_api_spec.py +++ b/test_elasticsearch/test_server/test_rest_api_spec.py @@ -25,6 +25,7 @@ import json import os import re +import subprocess import warnings import zipfile from typing import Tuple, Union @@ -35,6 +36,7 @@ from elasticsearch import ApiError, ElasticsearchWarning, RequestError from elasticsearch._sync.client.utils import _base64_auth_header +from elasticsearch._version import __versionstr__ from elasticsearch.compat import string_types # some params had to be changed in python, keep track of them so we can rename @@ -499,12 +501,29 @@ def remove_implicit_resolver(cls, tag_to_remove): # Construct the HTTP and Elasticsearch client http = urllib3.PoolManager(retries=urllib3.Retry(total=10)) - yaml_tests_url = ( - "https://api.github.com/repos/elastic/elasticsearch-clients-tests/zipball/main" - ) + branch_candidates = [] + if "ES_YAML_TESTS_BRANCH" in os.environ: + branch_candidates.append(os.environ["ES_YAML_TESTS_BRANCH"]) + git_branch = subprocess.getoutput("git branch --show-current") + if git_branch not in branch_candidates: + branch_candidates.append(git_branch) + package_version = __versionstr__.rsplit(".", 1)[0] + if package_version not in branch_candidates: + branch_candidates.append(package_version) + if "main" not in branch_candidates: + branch_candidates.append("main") + + response = None + branch = "main" + for branch in branch_candidates: + yaml_tests_url = f"https://api.github.com/repos/elastic/elasticsearch-clients-tests/zipball/%7Bbranch%7D" + response = http.request("GET", yaml_tests_url) + if response.status != 404: + break + assert response is not None # Download the zip and start reading YAML from the files in memory - package_zip = zipfile.ZipFile(io.BytesIO(http.request("GET", yaml_tests_url).data)) + package_zip = zipfile.ZipFile(io.BytesIO(response.data)) for yaml_file in package_zip.namelist(): if not re.match(r"^.*\/tests\/.*\.ya?ml$", yaml_file): @@ -562,7 +581,9 @@ def remove_implicit_resolver(cls, tag_to_remove): elif pytest_test_name in SKIPPED_TESTS or pytest_param_id in SKIPPED_TESTS: pytest_param["skip"] = True - YAML_TEST_SPECS.append(pytest.param(pytest_param, id=pytest_param_id)) + YAML_TEST_SPECS.append( + pytest.param(pytest_param, id=f"[{branch}]{pytest_param_id}") + ) except Exception as e: warnings.warn(f"Could not load REST API tests: {str(e)}") From 0d1783fa907e2d021a548a069c93ef8805fe09fd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 11:39:21 +0000 Subject: [PATCH 23/36] add bumpmatrix command to make.sh (#3168) (#3179) (cherry picked from commit 011442f564d75932c5fe964b57b8ce05954353cc) Co-authored-by: Miguel Grinberg --- .github/make.sh | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/.github/make.sh b/.github/make.sh index 79d785957..4a363e524 100755 --- a/.github/make.sh +++ b/.github/make.sh @@ -11,12 +11,13 @@ # # Targets: # --------------------------- -# assemble : build client artefacts with version -# bump : bump client internals to version -# codegen : generate endpoints -# docsgen : generate documentation -# examplegen : generate the doc examples -# clean : clean workspace +# assemble : build client artefacts with version +# bump : bump client internals to version +# bumpmatrix : bump stack version in test matrix to version +# codegen : generate endpoints +# docsgen : generate documentation +# examplegen : generate the doc examples +# clean : clean workspace # # ------------------------------------------------------- # @@ -24,7 +25,7 @@ # Bootstrap # ------------------------------------------------------- # -script_path=$(dirname "$(realpath -s "$0")") +script_path=$(dirname "$(realpath "$0")") repo=$(realpath "$script_path/../") # shellcheck disable=SC1090 @@ -100,6 +101,15 @@ case $CMD in # VERSION is BRANCH here for now TASK_ARGS=("$VERSION") ;; + bumpmatrix) + if [ -v $VERSION ]; then + echo -e "\033[31;1mTARGET: bumpmatrix -> missing version parameter\033[0m" + exit 1 + fi + echo -e "\033[36;1mTARGET: bump stack in test matrix to version $VERSION\033[0m" + TASK=bumpmatrix + TASK_ARGS=("$VERSION") + ;; *) echo -e "\nUsage:\n\t $CMD is not supported right now\n" exit 1 @@ -160,6 +170,13 @@ if [[ "$CMD" == "bump" ]]; then exit 0 fi +if [[ "$CMD" == "bumpmatrix" ]]; then + TEST_CONFIG_FILE=.buildkite/pipeline.yml + sed -E -i.bak 's/[0-9]+\.[0-9]+\.[0-9]+-SNAPSHOT/'$VERSION'/g' $TEST_CONFIG_FILE + rm ${TEST_CONFIG_FILE}.bak + exit 0 +fi + if [[ "$CMD" == "codegen" ]]; then docker run \ --rm -v $repo:/code/elasticsearch-py \ From 276312ad383029891955825773f30dca4dafb851 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:03:12 +0000 Subject: [PATCH 24/36] Ignore buildkite branch when not in a PR (#3180) (#3184) (cherry picked from commit 96be7a23516472aa067a113f35ec073b14aafa3a) Co-authored-by: Miguel Grinberg --- test_elasticsearch/test_server/test_rest_api_spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_elasticsearch/test_server/test_rest_api_spec.py b/test_elasticsearch/test_server/test_rest_api_spec.py index 6db2b1125..ea542efba 100644 --- a/test_elasticsearch/test_server/test_rest_api_spec.py +++ b/test_elasticsearch/test_server/test_rest_api_spec.py @@ -502,7 +502,7 @@ def remove_implicit_resolver(cls, tag_to_remove): http = urllib3.PoolManager(retries=urllib3.Retry(total=10)) branch_candidates = [] - if "ES_YAML_TESTS_BRANCH" in os.environ: + if "ES_YAML_TESTS_BRANCH" in os.environ and os.environ["ES_YAML_TESTS_BRANCH"]: branch_candidates.append(os.environ["ES_YAML_TESTS_BRANCH"]) git_branch = subprocess.getoutput("git branch --show-current") if git_branch not in branch_candidates: From 67b57d32592b405ef164f354651c6d5d1a2b648b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:23:59 +0000 Subject: [PATCH 25/36] Update link in dsl_how_to_guides.md (#3147) (#3149) This is a follow (cherry picked from commit 9a9c5927f606a8356b16eb7155c74c5eaeceb4c7) Co-authored-by: David Kilfoyle <41695641+kilfoyle@users.noreply.github.com> --- docs/reference/dsl_how_to_guides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/dsl_how_to_guides.md b/docs/reference/dsl_how_to_guides.md index 816e805e1..6cfec5398 100644 --- a/docs/reference/dsl_how_to_guides.md +++ b/docs/reference/dsl_how_to_guides.md @@ -927,7 +927,7 @@ first = Post.get(id=42) first.update(published=True, published_by='me') ``` -In case you wish to use a `painless` script to perform the update you can pass in the script string as `script` or the `id` of a [stored script](docs-content://explore-analyze/scripting/modules-scripting-using.md) via `script_id`. All additional keyword arguments to the `update` method will then be passed in as parameters of the script. The document will not be updated in place. +In case you wish to use a `painless` script to perform the update you can pass in the script string as `script` or the `id` of a [stored script](docs-content://explore-analyze/scripting/modules-scripting-store-and-retrieve.md) via `script_id`. All additional keyword arguments to the `update` method will then be passed in as parameters of the script. The document will not be updated in place. ```python # retrieve the document From 99099ad7d68598beb35d41899c7957a578c6e867 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:47:27 +0000 Subject: [PATCH 26/36] Do not use a default when getting the buildkite PR branch (#3185) (#3189) (cherry picked from commit 3eb91b0ab2bae9be17501d42e0288f41657b6ae4) Co-authored-by: Miguel Grinberg --- .buildkite/run-tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildkite/run-tests b/.buildkite/run-tests index 2f37ec9f0..a9077179e 100755 --- a/.buildkite/run-tests +++ b/.buildkite/run-tests @@ -9,7 +9,7 @@ export STACK_VERSION="${STACK_VERSION:=8.0.0-SNAPSHOT}" export TEST_SUITE="${TEST_SUITE:=platinum}" export PYTHON_VERSION="${PYTHON_VERSION:=3.14}" export PYTHON_CONNECTION_CLASS="${PYTHON_CONNECTION_CLASS:=urllib3}" -export ES_YAML_TESTS_BRANCH=${BUILDKITE_PULL_REQUEST_BASE_BRANCH:=main} +export ES_YAML_TESTS_BRANCH="${BUILDKITE_PULL_REQUEST_BASE_BRANCH}" script_path=$(dirname $(realpath $0)) source $script_path/functions/imports.sh From 90898b77ba626c5d4e731efba16373293b1c6f5a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 16:05:42 +0000 Subject: [PATCH 27/36] Fix missing assignment (#3151) (#3192) (cherry picked from commit fa8fe728e8044ec36710d00c0358eb9d94fdfdbc) Co-authored-by: celeste0-0sec Co-authored-by: Quentin Pradet --- elasticsearch/esql/esql.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elasticsearch/esql/esql.py b/elasticsearch/esql/esql.py index 501148e39..bfda4815d 100644 --- a/elasticsearch/esql/esql.py +++ b/elasticsearch/esql/esql.py @@ -140,7 +140,7 @@ def _format_id(id: FieldType, allow_patterns: bool = False) -> str: if re.fullmatch(r"[a-zA-Z_@][a-zA-Z0-9_\.]*", s): return s # this identifier needs to be escaped - s.replace("`", "``") + s = s.replace("`", "``") return f"`{s}`" @staticmethod From b19a4e001512248597c37b27bd36b4cda7f6e459 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 18:38:27 +0000 Subject: [PATCH 28/36] yaml tests: more improvements to branching logic (#3197) (#3201) (cherry picked from commit 01d1e81b0144fdcaaf9a075af1a168681d5b5447) Co-authored-by: Miguel Grinberg --- .buildkite/run-tests | 3 +++ test_elasticsearch/test_server/test_rest_api_spec.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.buildkite/run-tests b/.buildkite/run-tests index a9077179e..d2403c3a9 100755 --- a/.buildkite/run-tests +++ b/.buildkite/run-tests @@ -10,6 +10,9 @@ export TEST_SUITE="${TEST_SUITE:=platinum}" export PYTHON_VERSION="${PYTHON_VERSION:=3.14}" export PYTHON_CONNECTION_CLASS="${PYTHON_CONNECTION_CLASS:=urllib3}" export ES_YAML_TESTS_BRANCH="${BUILDKITE_PULL_REQUEST_BASE_BRANCH}" +if [[ ! -n "$ES_YAML_TESTS_BRANCH" ]]; then + export ES_YAML_TESTS_BRANCH="${BUILDKITE_BRANCH}" +fi script_path=$(dirname $(realpath $0)) source $script_path/functions/imports.sh diff --git a/test_elasticsearch/test_server/test_rest_api_spec.py b/test_elasticsearch/test_server/test_rest_api_spec.py index ea542efba..0706a70d9 100644 --- a/test_elasticsearch/test_server/test_rest_api_spec.py +++ b/test_elasticsearch/test_server/test_rest_api_spec.py @@ -505,10 +505,10 @@ def remove_implicit_resolver(cls, tag_to_remove): if "ES_YAML_TESTS_BRANCH" in os.environ and os.environ["ES_YAML_TESTS_BRANCH"]: branch_candidates.append(os.environ["ES_YAML_TESTS_BRANCH"]) git_branch = subprocess.getoutput("git branch --show-current") - if git_branch not in branch_candidates: + if git_branch and git_branch not in branch_candidates: branch_candidates.append(git_branch) package_version = __versionstr__.rsplit(".", 1)[0] - if package_version not in branch_candidates: + if package_version and package_version not in branch_candidates: branch_candidates.append(package_version) if "main" not in branch_candidates: branch_candidates.append("main") From 35c43e6fc36f21a8d0623f97254622eb64098d0d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:07:13 +0000 Subject: [PATCH 29/36] Add global variable support to YAML test expression parser (#3196) (#3205) (cherry picked from commit 42f1834a500d5cb0d5e634e6ffc47a8a419c4d84) Co-authored-by: Miguel Grinberg --- .../test_server/test_rest_api_spec.py | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/test_elasticsearch/test_server/test_rest_api_spec.py b/test_elasticsearch/test_server/test_rest_api_spec.py index 0706a70d9..7a8541c76 100644 --- a/test_elasticsearch/test_server/test_rest_api_spec.py +++ b/test_elasticsearch/test_server/test_rest_api_spec.py @@ -411,16 +411,21 @@ def _resolve(self, value): return value def _lookup(self, path): - # fetch the possibly nested value from last_response - value = self.last_response if path == "$body": - return value + return self.last_response + if path.startswith("$"): + value = None + else: + value = self.last_response path = path.replace(r"\.", "\1") for step in path.split("."): if not step: continue # We check body again to handle E.g. '$body.$backing_index.data_stream' - if step.startswith("$body"): + if step == "$body": + assert value is None + # fetch the possibly nested value from last_response + value = self.last_response continue step = step.replace("\1", ".") step = self._resolve(step) @@ -432,11 +437,15 @@ def _lookup(self, path): step = int(step) assert isinstance(value, list) assert len(value) > step + value = value[step] elif step == "_arbitrary_key_": return list(value.keys())[0] - else: + elif isinstance(step, string_types) and isinstance(value, dict): assert step in value - value = value[step] + value = value[step] + else: + assert value is None + value = step return value def _feature_enabled(self, name): From b80669b135abd8004a2f81cfe0a332a8765119d2 Mon Sep 17 00:00:00 2001 From: Elastic Machine Date: Mon, 1 Dec 2025 12:19:11 +0100 Subject: [PATCH 30/36] Bumps stack to version 9.2.2-SNAPSHOT (#3209) --- .buildkite/pipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 71154ec4a..bcd28805a 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -5,7 +5,7 @@ steps: env: PYTHON_VERSION: "{{ matrix.python }}" TEST_SUITE: "platinum" - STACK_VERSION: "9.2.0-SNAPSHOT" + STACK_VERSION: "9.2.2-SNAPSHOT" PYTHON_CONNECTION_CLASS: "{{ matrix.connection }}" NOX_SESSION: "{{ matrix.nox_session }}" matrix: From b45e7d92126c7998ce6e5c7a92a4a32706f029dd Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Wed, 3 Dec 2025 15:53:48 +0000 Subject: [PATCH 31/36] Add warnings for private APIs (#3212) (#3213) --- elasticsearch/_async/client/__init__.py | 4 +- elasticsearch/_async/client/autoscaling.py | 13 +++- elasticsearch/_async/client/cat.py | 4 +- elasticsearch/_async/client/connector.py | 63 ++++++++++--------- elasticsearch/_async/client/esql.py | 6 +- elasticsearch/_async/client/features.py | 4 +- elasticsearch/_async/client/fleet.py | 6 +- elasticsearch/_async/client/indices.py | 16 ++--- elasticsearch/_async/client/ml.py | 11 +++- elasticsearch/_async/client/monitoring.py | 3 +- elasticsearch/_async/client/nodes.py | 6 +- elasticsearch/_async/client/project.py | 4 +- elasticsearch/_async/client/rollup.py | 18 +++--- .../_async/client/search_application.py | 22 +++---- .../_async/client/searchable_snapshots.py | 6 +- elasticsearch/_async/client/shutdown.py | 12 +++- elasticsearch/_async/client/simulate.py | 4 +- elasticsearch/_async/client/snapshot.py | 4 +- elasticsearch/_async/client/streams.py | 8 +-- elasticsearch/_async/client/tasks.py | 8 +-- elasticsearch/_async/client/utils.py | 6 +- elasticsearch/_sync/client/__init__.py | 4 +- elasticsearch/_sync/client/autoscaling.py | 13 +++- elasticsearch/_sync/client/cat.py | 4 +- elasticsearch/_sync/client/connector.py | 63 ++++++++++--------- elasticsearch/_sync/client/esql.py | 6 +- elasticsearch/_sync/client/features.py | 4 +- elasticsearch/_sync/client/fleet.py | 6 +- elasticsearch/_sync/client/indices.py | 16 ++--- elasticsearch/_sync/client/ml.py | 11 +++- elasticsearch/_sync/client/monitoring.py | 3 +- elasticsearch/_sync/client/nodes.py | 6 +- elasticsearch/_sync/client/project.py | 4 +- elasticsearch/_sync/client/rollup.py | 18 +++--- .../_sync/client/search_application.py | 22 +++---- .../_sync/client/searchable_snapshots.py | 6 +- elasticsearch/_sync/client/shutdown.py | 12 +++- elasticsearch/_sync/client/simulate.py | 4 +- elasticsearch/_sync/client/snapshot.py | 4 +- elasticsearch/_sync/client/streams.py | 8 +-- elasticsearch/_sync/client/tasks.py | 8 +-- elasticsearch/_sync/client/utils.py | 18 +++++- test_elasticsearch/test_client/test_utils.py | 41 ++++++++++-- 43 files changed, 310 insertions(+), 199 deletions(-) diff --git a/elasticsearch/_async/client/__init__.py b/elasticsearch/_async/client/__init__.py index 15213a6c9..02d5b586c 100644 --- a/elasticsearch/_async/client/__init__.py +++ b/elasticsearch/_async/client/__init__.py @@ -85,9 +85,9 @@ CLIENT_META_SERVICE, SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, client_node_configs, is_requests_http_auth, is_requests_node_class, @@ -4159,7 +4159,7 @@ async def render_search_template( @_rewrite_parameters( body_fields=("context", "context_setup", "script"), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def scripts_painless_execute( self, *, diff --git a/elasticsearch/_async/client/autoscaling.py b/elasticsearch/_async/client/autoscaling.py index 5b41caa38..50067cdde 100644 --- a/elasticsearch/_async/client/autoscaling.py +++ b/elasticsearch/_async/client/autoscaling.py @@ -20,12 +20,20 @@ from elastic_transport import ObjectApiResponse from ._base import NamespacedClient -from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters +from .utils import ( + SKIP_IN_PATH, + Stability, + Visibility, + _availability_warning, + _quote, + _rewrite_parameters, +) class AutoscalingClient(NamespacedClient): @_rewrite_parameters() + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) async def delete_autoscaling_policy( self, *, @@ -81,6 +89,7 @@ async def delete_autoscaling_policy( ) @_rewrite_parameters() + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) async def get_autoscaling_capacity( self, *, @@ -134,6 +143,7 @@ async def get_autoscaling_capacity( ) @_rewrite_parameters() + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) async def get_autoscaling_policy( self, *, @@ -186,6 +196,7 @@ async def get_autoscaling_policy( @_rewrite_parameters( body_name="policy", ) + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) async def put_autoscaling_policy( self, *, diff --git a/elasticsearch/_async/client/cat.py b/elasticsearch/_async/client/cat.py index f033a97ec..02534d016 100644 --- a/elasticsearch/_async/client/cat.py +++ b/elasticsearch/_async/client/cat.py @@ -23,9 +23,9 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) @@ -3868,7 +3868,7 @@ async def snapshots( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def tasks( self, *, diff --git a/elasticsearch/_async/client/connector.py b/elasticsearch/_async/client/connector.py index 70e468db7..797371850 100644 --- a/elasticsearch/_async/client/connector.py +++ b/elasticsearch/_async/client/connector.py @@ -23,16 +23,17 @@ from .utils import ( SKIP_IN_PATH, Stability, + Visibility, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) class ConnectorClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def check_in( self, *, @@ -77,7 +78,7 @@ async def check_in( ) @_rewrite_parameters() - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def delete( self, *, @@ -134,7 +135,7 @@ async def delete( ) @_rewrite_parameters() - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def get( self, *, @@ -199,7 +200,7 @@ async def get( "sync_cursor", ), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL, Visibility.PRIVATE) async def last_sync( self, *, @@ -333,7 +334,7 @@ async def last_sync( @_rewrite_parameters( parameter_aliases={"from": "from_"}, ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def list( self, *, @@ -416,7 +417,7 @@ async def list( "service_type", ), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def post( self, *, @@ -500,7 +501,7 @@ async def post( "service_type", ), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def put( self, *, @@ -580,7 +581,7 @@ async def put( ) @_rewrite_parameters() - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def sync_job_cancel( self, *, @@ -630,7 +631,7 @@ async def sync_job_cancel( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def sync_job_check_in( self, *, @@ -684,7 +685,7 @@ async def sync_job_check_in( @_rewrite_parameters( body_fields=("worker_hostname", "sync_cursor"), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def sync_job_claim( self, *, @@ -753,7 +754,7 @@ async def sync_job_claim( ) @_rewrite_parameters() - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def sync_job_delete( self, *, @@ -804,7 +805,7 @@ async def sync_job_delete( @_rewrite_parameters( body_fields=("error",), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def sync_job_error( self, *, @@ -863,7 +864,7 @@ async def sync_job_error( ) @_rewrite_parameters() - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def sync_job_get( self, *, @@ -911,7 +912,7 @@ async def sync_job_get( @_rewrite_parameters( parameter_aliases={"from": "from_"}, ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def sync_job_list( self, *, @@ -994,7 +995,7 @@ async def sync_job_list( @_rewrite_parameters( body_fields=("id", "job_type", "trigger_method"), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def sync_job_post( self, *, @@ -1066,7 +1067,7 @@ async def sync_job_post( "total_document_count", ), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def sync_job_update_stats( self, *, @@ -1160,7 +1161,7 @@ async def sync_job_update_stats( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def update_active_filtering( self, *, @@ -1207,7 +1208,7 @@ async def update_active_filtering( @_rewrite_parameters( body_fields=("api_key_id", "api_key_secret_id"), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def update_api_key_id( self, *, @@ -1269,7 +1270,7 @@ async def update_api_key_id( @_rewrite_parameters( body_fields=("configuration", "values"), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def update_configuration( self, *, @@ -1328,7 +1329,7 @@ async def update_configuration( @_rewrite_parameters( body_fields=("error",), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def update_error( self, *, @@ -1387,7 +1388,7 @@ async def update_error( @_rewrite_parameters( body_fields=("features",), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def update_features( self, *, @@ -1455,7 +1456,7 @@ async def update_features( @_rewrite_parameters( body_fields=("advanced_snippet", "filtering", "rules"), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def update_filtering( self, *, @@ -1520,7 +1521,7 @@ async def update_filtering( @_rewrite_parameters( body_fields=("validation",), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def update_filtering_validation( self, *, @@ -1577,7 +1578,7 @@ async def update_filtering_validation( @_rewrite_parameters( body_fields=("index_name",), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def update_index_name( self, *, @@ -1634,7 +1635,7 @@ async def update_index_name( @_rewrite_parameters( body_fields=("description", "name"), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def update_name( self, *, @@ -1692,7 +1693,7 @@ async def update_name( @_rewrite_parameters( body_fields=("is_native",), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def update_native( self, *, @@ -1748,7 +1749,7 @@ async def update_native( @_rewrite_parameters( body_fields=("pipeline",), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def update_pipeline( self, *, @@ -1805,7 +1806,7 @@ async def update_pipeline( @_rewrite_parameters( body_fields=("scheduling",), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def update_scheduling( self, *, @@ -1861,7 +1862,7 @@ async def update_scheduling( @_rewrite_parameters( body_fields=("service_type",), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def update_service_type( self, *, @@ -1917,7 +1918,7 @@ async def update_service_type( @_rewrite_parameters( body_fields=("status",), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def update_status( self, *, diff --git a/elasticsearch/_async/client/esql.py b/elasticsearch/_async/client/esql.py index 79f28a8a3..8d75f809c 100644 --- a/elasticsearch/_async/client/esql.py +++ b/elasticsearch/_async/client/esql.py @@ -23,9 +23,9 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) if t.TYPE_CHECKING: @@ -404,7 +404,7 @@ async def async_query_stop( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def get_query( self, *, @@ -449,7 +449,7 @@ async def get_query( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def list_queries( self, *, diff --git a/elasticsearch/_async/client/features.py b/elasticsearch/_async/client/features.py index 2fb86fe23..09421cda8 100644 --- a/elasticsearch/_async/client/features.py +++ b/elasticsearch/_async/client/features.py @@ -20,7 +20,7 @@ from elastic_transport import ObjectApiResponse from ._base import NamespacedClient -from .utils import Stability, _rewrite_parameters, _stability_warning +from .utils import Stability, _availability_warning, _rewrite_parameters class FeaturesClient(NamespacedClient): @@ -76,7 +76,7 @@ async def get_features( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def reset_features( self, *, diff --git a/elasticsearch/_async/client/fleet.py b/elasticsearch/_async/client/fleet.py index de2be54cb..915803884 100644 --- a/elasticsearch/_async/client/fleet.py +++ b/elasticsearch/_async/client/fleet.py @@ -23,9 +23,9 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) @@ -101,7 +101,7 @@ async def global_checkpoints( @_rewrite_parameters( body_name="searches", ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def msearch( self, *, @@ -293,7 +293,7 @@ async def msearch( "from": "from_", }, ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def search( self, *, diff --git a/elasticsearch/_async/client/indices.py b/elasticsearch/_async/client/indices.py index 082b860c3..9421ff940 100644 --- a/elasticsearch/_async/client/indices.py +++ b/elasticsearch/_async/client/indices.py @@ -23,9 +23,9 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) @@ -244,7 +244,7 @@ async def analyze( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def cancel_migrate_reindex( self, *, @@ -778,7 +778,7 @@ async def create_data_stream( @_rewrite_parameters( body_name="create_from", ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def create_from( self, *, @@ -1357,7 +1357,7 @@ async def delete_template( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def disk_usage( self, *, @@ -1450,7 +1450,7 @@ async def disk_usage( @_rewrite_parameters( body_name="config", ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def downsample( self, *, @@ -1862,7 +1862,7 @@ async def explain_data_lifecycle( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def field_usage_stats( self, *, @@ -2947,7 +2947,7 @@ async def get_mapping( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def get_migrate_reindex_status( self, *, @@ -3163,7 +3163,7 @@ async def get_template( @_rewrite_parameters( body_name="reindex", ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def migrate_reindex( self, *, diff --git a/elasticsearch/_async/client/ml.py b/elasticsearch/_async/client/ml.py index 4fba07482..f6cd5d834 100644 --- a/elasticsearch/_async/client/ml.py +++ b/elasticsearch/_async/client/ml.py @@ -20,7 +20,14 @@ from elastic_transport import ObjectApiResponse from ._base import NamespacedClient -from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters +from .utils import ( + SKIP_IN_PATH, + Stability, + Visibility, + _availability_warning, + _quote, + _rewrite_parameters, +) class MlClient(NamespacedClient): @@ -5692,6 +5699,7 @@ async def upgrade_job_snapshot( "results_index_name", ), ) + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) async def validate( self, *, @@ -5773,6 +5781,7 @@ async def validate( @_rewrite_parameters( body_name="detector", ) + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) async def validate_detector( self, *, diff --git a/elasticsearch/_async/client/monitoring.py b/elasticsearch/_async/client/monitoring.py index afc6406da..541bb0319 100644 --- a/elasticsearch/_async/client/monitoring.py +++ b/elasticsearch/_async/client/monitoring.py @@ -20,7 +20,7 @@ from elastic_transport import ObjectApiResponse from ._base import NamespacedClient -from .utils import _rewrite_parameters +from .utils import Stability, Visibility, _availability_warning, _rewrite_parameters class MonitoringClient(NamespacedClient): @@ -28,6 +28,7 @@ class MonitoringClient(NamespacedClient): @_rewrite_parameters( body_name="operations", ) + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) async def bulk( self, *, diff --git a/elasticsearch/_async/client/nodes.py b/elasticsearch/_async/client/nodes.py index 1ed5cd5eb..f6b92d663 100644 --- a/elasticsearch/_async/client/nodes.py +++ b/elasticsearch/_async/client/nodes.py @@ -23,16 +23,16 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) class NodesClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def clear_repositories_metering_archive( self, *, @@ -86,7 +86,7 @@ async def clear_repositories_metering_archive( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def get_repositories_metering_info( self, *, diff --git a/elasticsearch/_async/client/project.py b/elasticsearch/_async/client/project.py index 6e1e1a63a..dee5a2c20 100644 --- a/elasticsearch/_async/client/project.py +++ b/elasticsearch/_async/client/project.py @@ -22,15 +22,15 @@ from ._base import NamespacedClient from .utils import ( Stability, + _availability_warning, _rewrite_parameters, - _stability_warning, ) class ProjectClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def tags( self, *, diff --git a/elasticsearch/_async/client/rollup.py b/elasticsearch/_async/client/rollup.py index ea1ace0dc..83503e9b5 100644 --- a/elasticsearch/_async/client/rollup.py +++ b/elasticsearch/_async/client/rollup.py @@ -23,16 +23,16 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) class RollupClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def delete_job( self, *, @@ -95,7 +95,7 @@ async def delete_job( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def get_jobs( self, *, @@ -147,7 +147,7 @@ async def get_jobs( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def get_rollup_caps( self, *, @@ -203,7 +203,7 @@ async def get_rollup_caps( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def get_rollup_index_caps( self, *, @@ -266,7 +266,7 @@ async def get_rollup_index_caps( ), ignore_deprecated_options={"headers"}, ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def put_job( self, *, @@ -392,7 +392,7 @@ async def put_job( @_rewrite_parameters( body_fields=("aggregations", "aggs", "query", "size"), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def rollup_search( self, *, @@ -482,7 +482,7 @@ async def rollup_search( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def start_job( self, *, @@ -528,7 +528,7 @@ async def start_job( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def stop_job( self, *, diff --git a/elasticsearch/_async/client/search_application.py b/elasticsearch/_async/client/search_application.py index 30352bd67..c3a5bf629 100644 --- a/elasticsearch/_async/client/search_application.py +++ b/elasticsearch/_async/client/search_application.py @@ -23,16 +23,16 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) class SearchApplicationClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def delete( self, *, @@ -77,7 +77,7 @@ async def delete( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def delete_behavioral_analytics( self, *, @@ -122,7 +122,7 @@ async def delete_behavioral_analytics( ) @_rewrite_parameters() - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def get( self, *, @@ -166,7 +166,7 @@ async def get( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def get_behavioral_analytics( self, *, @@ -215,7 +215,7 @@ async def get_behavioral_analytics( @_rewrite_parameters( parameter_aliases={"from": "from_"}, ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def list( self, *, @@ -270,7 +270,7 @@ async def list( @_rewrite_parameters( body_name="payload", ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def post_behavioral_analytics_event( self, *, @@ -338,7 +338,7 @@ async def post_behavioral_analytics_event( @_rewrite_parameters( body_name="search_application", ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def put( self, *, @@ -398,7 +398,7 @@ async def put( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def put_behavioral_analytics( self, *, @@ -445,7 +445,7 @@ async def put_behavioral_analytics( body_fields=("params",), ignore_deprecated_options={"params"}, ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def render_query( self, *, @@ -510,7 +510,7 @@ async def render_query( body_fields=("params",), ignore_deprecated_options={"params"}, ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) async def search( self, *, diff --git a/elasticsearch/_async/client/searchable_snapshots.py b/elasticsearch/_async/client/searchable_snapshots.py index 9b6902fac..822fd8785 100644 --- a/elasticsearch/_async/client/searchable_snapshots.py +++ b/elasticsearch/_async/client/searchable_snapshots.py @@ -23,16 +23,16 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) class SearchableSnapshotsClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def cache_stats( self, *, @@ -84,7 +84,7 @@ async def cache_stats( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def clear_cache( self, *, diff --git a/elasticsearch/_async/client/shutdown.py b/elasticsearch/_async/client/shutdown.py index 9502d1fe6..231d1a208 100644 --- a/elasticsearch/_async/client/shutdown.py +++ b/elasticsearch/_async/client/shutdown.py @@ -20,12 +20,20 @@ from elastic_transport import ObjectApiResponse from ._base import NamespacedClient -from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters +from .utils import ( + SKIP_IN_PATH, + Stability, + Visibility, + _availability_warning, + _quote, + _rewrite_parameters, +) class ShutdownClient(NamespacedClient): @_rewrite_parameters() + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) async def delete_node( self, *, @@ -86,6 +94,7 @@ async def delete_node( ) @_rewrite_parameters() + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) async def get_node( self, *, @@ -144,6 +153,7 @@ async def get_node( @_rewrite_parameters( body_fields=("reason", "type", "allocation_delay", "target_node_name"), ) + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) async def put_node( self, *, diff --git a/elasticsearch/_async/client/simulate.py b/elasticsearch/_async/client/simulate.py index ae59515ed..25d7ea826 100644 --- a/elasticsearch/_async/client/simulate.py +++ b/elasticsearch/_async/client/simulate.py @@ -23,9 +23,9 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) @@ -40,7 +40,7 @@ class SimulateClient(NamespacedClient): "pipeline_substitutions", ), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def ingest( self, *, diff --git a/elasticsearch/_async/client/snapshot.py b/elasticsearch/_async/client/snapshot.py index 249bb0100..2c8d7f00d 100644 --- a/elasticsearch/_async/client/snapshot.py +++ b/elasticsearch/_async/client/snapshot.py @@ -23,9 +23,9 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) @@ -955,7 +955,7 @@ async def repository_analyze( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def repository_verify_integrity( self, *, diff --git a/elasticsearch/_async/client/streams.py b/elasticsearch/_async/client/streams.py index 65e934646..1310bfa2b 100644 --- a/elasticsearch/_async/client/streams.py +++ b/elasticsearch/_async/client/streams.py @@ -23,15 +23,15 @@ from ._base import NamespacedClient from .utils import ( Stability, + _availability_warning, _rewrite_parameters, - _stability_warning, ) class StreamsClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def logs_disable( self, *, @@ -83,7 +83,7 @@ async def logs_disable( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def logs_enable( self, *, @@ -138,7 +138,7 @@ async def logs_enable( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def status( self, *, diff --git a/elasticsearch/_async/client/tasks.py b/elasticsearch/_async/client/tasks.py index 96230cc4c..93376baa1 100644 --- a/elasticsearch/_async/client/tasks.py +++ b/elasticsearch/_async/client/tasks.py @@ -23,16 +23,16 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) class TasksClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def cancel( self, *, @@ -106,7 +106,7 @@ async def cancel( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def get( self, *, @@ -164,7 +164,7 @@ async def get( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) async def list( self, *, diff --git a/elasticsearch/_async/client/utils.py b/elasticsearch/_async/client/utils.py index 97918d9e4..3ccd0fc36 100644 --- a/elasticsearch/_async/client/utils.py +++ b/elasticsearch/_async/client/utils.py @@ -21,11 +21,12 @@ CLIENT_META_SERVICE, SKIP_IN_PATH, Stability, + Visibility, + _availability_warning, _base64_auth_header, _quote, _quote_query, _rewrite_parameters, - _stability_warning, client_node_configs, is_requests_http_auth, is_requests_node_class, @@ -40,9 +41,10 @@ "_TYPE_HOSTS", "SKIP_IN_PATH", "Stability", + "Visibility", "client_node_configs", "_rewrite_parameters", - "_stability_warning", + "_availability_warning", "is_requests_http_auth", "is_requests_node_class", ] diff --git a/elasticsearch/_sync/client/__init__.py b/elasticsearch/_sync/client/__init__.py index bd41b710b..875221797 100644 --- a/elasticsearch/_sync/client/__init__.py +++ b/elasticsearch/_sync/client/__init__.py @@ -85,9 +85,9 @@ CLIENT_META_SERVICE, SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, client_node_configs, is_requests_http_auth, is_requests_node_class, @@ -4159,7 +4159,7 @@ def render_search_template( @_rewrite_parameters( body_fields=("context", "context_setup", "script"), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def scripts_painless_execute( self, *, diff --git a/elasticsearch/_sync/client/autoscaling.py b/elasticsearch/_sync/client/autoscaling.py index 98fe5d109..56b9433cc 100644 --- a/elasticsearch/_sync/client/autoscaling.py +++ b/elasticsearch/_sync/client/autoscaling.py @@ -20,12 +20,20 @@ from elastic_transport import ObjectApiResponse from ._base import NamespacedClient -from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters +from .utils import ( + SKIP_IN_PATH, + Stability, + Visibility, + _availability_warning, + _quote, + _rewrite_parameters, +) class AutoscalingClient(NamespacedClient): @_rewrite_parameters() + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) def delete_autoscaling_policy( self, *, @@ -81,6 +89,7 @@ def delete_autoscaling_policy( ) @_rewrite_parameters() + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) def get_autoscaling_capacity( self, *, @@ -134,6 +143,7 @@ def get_autoscaling_capacity( ) @_rewrite_parameters() + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) def get_autoscaling_policy( self, *, @@ -186,6 +196,7 @@ def get_autoscaling_policy( @_rewrite_parameters( body_name="policy", ) + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) def put_autoscaling_policy( self, *, diff --git a/elasticsearch/_sync/client/cat.py b/elasticsearch/_sync/client/cat.py index 2461c6097..6132b3c04 100644 --- a/elasticsearch/_sync/client/cat.py +++ b/elasticsearch/_sync/client/cat.py @@ -23,9 +23,9 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) @@ -3868,7 +3868,7 @@ def snapshots( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def tasks( self, *, diff --git a/elasticsearch/_sync/client/connector.py b/elasticsearch/_sync/client/connector.py index c28a4c823..d3f3e5985 100644 --- a/elasticsearch/_sync/client/connector.py +++ b/elasticsearch/_sync/client/connector.py @@ -23,16 +23,17 @@ from .utils import ( SKIP_IN_PATH, Stability, + Visibility, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) class ConnectorClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def check_in( self, *, @@ -77,7 +78,7 @@ def check_in( ) @_rewrite_parameters() - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def delete( self, *, @@ -134,7 +135,7 @@ def delete( ) @_rewrite_parameters() - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def get( self, *, @@ -199,7 +200,7 @@ def get( "sync_cursor", ), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL, Visibility.PRIVATE) def last_sync( self, *, @@ -333,7 +334,7 @@ def last_sync( @_rewrite_parameters( parameter_aliases={"from": "from_"}, ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def list( self, *, @@ -416,7 +417,7 @@ def list( "service_type", ), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def post( self, *, @@ -500,7 +501,7 @@ def post( "service_type", ), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def put( self, *, @@ -580,7 +581,7 @@ def put( ) @_rewrite_parameters() - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def sync_job_cancel( self, *, @@ -630,7 +631,7 @@ def sync_job_cancel( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def sync_job_check_in( self, *, @@ -684,7 +685,7 @@ def sync_job_check_in( @_rewrite_parameters( body_fields=("worker_hostname", "sync_cursor"), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def sync_job_claim( self, *, @@ -753,7 +754,7 @@ def sync_job_claim( ) @_rewrite_parameters() - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def sync_job_delete( self, *, @@ -804,7 +805,7 @@ def sync_job_delete( @_rewrite_parameters( body_fields=("error",), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def sync_job_error( self, *, @@ -863,7 +864,7 @@ def sync_job_error( ) @_rewrite_parameters() - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def sync_job_get( self, *, @@ -911,7 +912,7 @@ def sync_job_get( @_rewrite_parameters( parameter_aliases={"from": "from_"}, ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def sync_job_list( self, *, @@ -994,7 +995,7 @@ def sync_job_list( @_rewrite_parameters( body_fields=("id", "job_type", "trigger_method"), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def sync_job_post( self, *, @@ -1066,7 +1067,7 @@ def sync_job_post( "total_document_count", ), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def sync_job_update_stats( self, *, @@ -1160,7 +1161,7 @@ def sync_job_update_stats( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def update_active_filtering( self, *, @@ -1207,7 +1208,7 @@ def update_active_filtering( @_rewrite_parameters( body_fields=("api_key_id", "api_key_secret_id"), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def update_api_key_id( self, *, @@ -1269,7 +1270,7 @@ def update_api_key_id( @_rewrite_parameters( body_fields=("configuration", "values"), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def update_configuration( self, *, @@ -1328,7 +1329,7 @@ def update_configuration( @_rewrite_parameters( body_fields=("error",), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def update_error( self, *, @@ -1387,7 +1388,7 @@ def update_error( @_rewrite_parameters( body_fields=("features",), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def update_features( self, *, @@ -1455,7 +1456,7 @@ def update_features( @_rewrite_parameters( body_fields=("advanced_snippet", "filtering", "rules"), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def update_filtering( self, *, @@ -1520,7 +1521,7 @@ def update_filtering( @_rewrite_parameters( body_fields=("validation",), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def update_filtering_validation( self, *, @@ -1577,7 +1578,7 @@ def update_filtering_validation( @_rewrite_parameters( body_fields=("index_name",), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def update_index_name( self, *, @@ -1634,7 +1635,7 @@ def update_index_name( @_rewrite_parameters( body_fields=("description", "name"), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def update_name( self, *, @@ -1692,7 +1693,7 @@ def update_name( @_rewrite_parameters( body_fields=("is_native",), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def update_native( self, *, @@ -1748,7 +1749,7 @@ def update_native( @_rewrite_parameters( body_fields=("pipeline",), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def update_pipeline( self, *, @@ -1805,7 +1806,7 @@ def update_pipeline( @_rewrite_parameters( body_fields=("scheduling",), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def update_scheduling( self, *, @@ -1861,7 +1862,7 @@ def update_scheduling( @_rewrite_parameters( body_fields=("service_type",), ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def update_service_type( self, *, @@ -1917,7 +1918,7 @@ def update_service_type( @_rewrite_parameters( body_fields=("status",), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def update_status( self, *, diff --git a/elasticsearch/_sync/client/esql.py b/elasticsearch/_sync/client/esql.py index b76111b20..ae42d4f69 100644 --- a/elasticsearch/_sync/client/esql.py +++ b/elasticsearch/_sync/client/esql.py @@ -23,9 +23,9 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) if t.TYPE_CHECKING: @@ -404,7 +404,7 @@ def async_query_stop( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def get_query( self, *, @@ -449,7 +449,7 @@ def get_query( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def list_queries( self, *, diff --git a/elasticsearch/_sync/client/features.py b/elasticsearch/_sync/client/features.py index 85432324e..3107d6052 100644 --- a/elasticsearch/_sync/client/features.py +++ b/elasticsearch/_sync/client/features.py @@ -20,7 +20,7 @@ from elastic_transport import ObjectApiResponse from ._base import NamespacedClient -from .utils import Stability, _rewrite_parameters, _stability_warning +from .utils import Stability, _availability_warning, _rewrite_parameters class FeaturesClient(NamespacedClient): @@ -76,7 +76,7 @@ def get_features( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def reset_features( self, *, diff --git a/elasticsearch/_sync/client/fleet.py b/elasticsearch/_sync/client/fleet.py index c6fdaa4a4..5b0096f81 100644 --- a/elasticsearch/_sync/client/fleet.py +++ b/elasticsearch/_sync/client/fleet.py @@ -23,9 +23,9 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) @@ -101,7 +101,7 @@ def global_checkpoints( @_rewrite_parameters( body_name="searches", ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def msearch( self, *, @@ -293,7 +293,7 @@ def msearch( "from": "from_", }, ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def search( self, *, diff --git a/elasticsearch/_sync/client/indices.py b/elasticsearch/_sync/client/indices.py index ffb05d766..673dc6443 100644 --- a/elasticsearch/_sync/client/indices.py +++ b/elasticsearch/_sync/client/indices.py @@ -23,9 +23,9 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) @@ -244,7 +244,7 @@ def analyze( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def cancel_migrate_reindex( self, *, @@ -778,7 +778,7 @@ def create_data_stream( @_rewrite_parameters( body_name="create_from", ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def create_from( self, *, @@ -1357,7 +1357,7 @@ def delete_template( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def disk_usage( self, *, @@ -1450,7 +1450,7 @@ def disk_usage( @_rewrite_parameters( body_name="config", ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def downsample( self, *, @@ -1862,7 +1862,7 @@ def explain_data_lifecycle( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def field_usage_stats( self, *, @@ -2947,7 +2947,7 @@ def get_mapping( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def get_migrate_reindex_status( self, *, @@ -3163,7 +3163,7 @@ def get_template( @_rewrite_parameters( body_name="reindex", ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def migrate_reindex( self, *, diff --git a/elasticsearch/_sync/client/ml.py b/elasticsearch/_sync/client/ml.py index 08ef976fb..2bdc42926 100644 --- a/elasticsearch/_sync/client/ml.py +++ b/elasticsearch/_sync/client/ml.py @@ -20,7 +20,14 @@ from elastic_transport import ObjectApiResponse from ._base import NamespacedClient -from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters +from .utils import ( + SKIP_IN_PATH, + Stability, + Visibility, + _availability_warning, + _quote, + _rewrite_parameters, +) class MlClient(NamespacedClient): @@ -5692,6 +5699,7 @@ def upgrade_job_snapshot( "results_index_name", ), ) + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) def validate( self, *, @@ -5773,6 +5781,7 @@ def validate( @_rewrite_parameters( body_name="detector", ) + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) def validate_detector( self, *, diff --git a/elasticsearch/_sync/client/monitoring.py b/elasticsearch/_sync/client/monitoring.py index 59cee2235..daead423d 100644 --- a/elasticsearch/_sync/client/monitoring.py +++ b/elasticsearch/_sync/client/monitoring.py @@ -20,7 +20,7 @@ from elastic_transport import ObjectApiResponse from ._base import NamespacedClient -from .utils import _rewrite_parameters +from .utils import Stability, Visibility, _availability_warning, _rewrite_parameters class MonitoringClient(NamespacedClient): @@ -28,6 +28,7 @@ class MonitoringClient(NamespacedClient): @_rewrite_parameters( body_name="operations", ) + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) def bulk( self, *, diff --git a/elasticsearch/_sync/client/nodes.py b/elasticsearch/_sync/client/nodes.py index 9cf656d16..8a754d2f3 100644 --- a/elasticsearch/_sync/client/nodes.py +++ b/elasticsearch/_sync/client/nodes.py @@ -23,16 +23,16 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) class NodesClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def clear_repositories_metering_archive( self, *, @@ -86,7 +86,7 @@ def clear_repositories_metering_archive( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def get_repositories_metering_info( self, *, diff --git a/elasticsearch/_sync/client/project.py b/elasticsearch/_sync/client/project.py index 57b4e8218..04b3973cd 100644 --- a/elasticsearch/_sync/client/project.py +++ b/elasticsearch/_sync/client/project.py @@ -22,15 +22,15 @@ from ._base import NamespacedClient from .utils import ( Stability, + _availability_warning, _rewrite_parameters, - _stability_warning, ) class ProjectClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def tags( self, *, diff --git a/elasticsearch/_sync/client/rollup.py b/elasticsearch/_sync/client/rollup.py index 8f098e2ff..44b8b92ba 100644 --- a/elasticsearch/_sync/client/rollup.py +++ b/elasticsearch/_sync/client/rollup.py @@ -23,16 +23,16 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) class RollupClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def delete_job( self, *, @@ -95,7 +95,7 @@ def delete_job( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def get_jobs( self, *, @@ -147,7 +147,7 @@ def get_jobs( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def get_rollup_caps( self, *, @@ -203,7 +203,7 @@ def get_rollup_caps( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def get_rollup_index_caps( self, *, @@ -266,7 +266,7 @@ def get_rollup_index_caps( ), ignore_deprecated_options={"headers"}, ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def put_job( self, *, @@ -392,7 +392,7 @@ def put_job( @_rewrite_parameters( body_fields=("aggregations", "aggs", "query", "size"), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def rollup_search( self, *, @@ -482,7 +482,7 @@ def rollup_search( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def start_job( self, *, @@ -528,7 +528,7 @@ def start_job( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def stop_job( self, *, diff --git a/elasticsearch/_sync/client/search_application.py b/elasticsearch/_sync/client/search_application.py index b81980475..601498096 100644 --- a/elasticsearch/_sync/client/search_application.py +++ b/elasticsearch/_sync/client/search_application.py @@ -23,16 +23,16 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) class SearchApplicationClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def delete( self, *, @@ -77,7 +77,7 @@ def delete( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def delete_behavioral_analytics( self, *, @@ -122,7 +122,7 @@ def delete_behavioral_analytics( ) @_rewrite_parameters() - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def get( self, *, @@ -166,7 +166,7 @@ def get( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def get_behavioral_analytics( self, *, @@ -215,7 +215,7 @@ def get_behavioral_analytics( @_rewrite_parameters( parameter_aliases={"from": "from_"}, ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def list( self, *, @@ -270,7 +270,7 @@ def list( @_rewrite_parameters( body_name="payload", ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def post_behavioral_analytics_event( self, *, @@ -338,7 +338,7 @@ def post_behavioral_analytics_event( @_rewrite_parameters( body_name="search_application", ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def put( self, *, @@ -398,7 +398,7 @@ def put( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def put_behavioral_analytics( self, *, @@ -445,7 +445,7 @@ def put_behavioral_analytics( body_fields=("params",), ignore_deprecated_options={"params"}, ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def render_query( self, *, @@ -510,7 +510,7 @@ def render_query( body_fields=("params",), ignore_deprecated_options={"params"}, ) - @_stability_warning(Stability.BETA) + @_availability_warning(Stability.BETA) def search( self, *, diff --git a/elasticsearch/_sync/client/searchable_snapshots.py b/elasticsearch/_sync/client/searchable_snapshots.py index 2160988c0..41231c456 100644 --- a/elasticsearch/_sync/client/searchable_snapshots.py +++ b/elasticsearch/_sync/client/searchable_snapshots.py @@ -23,16 +23,16 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) class SearchableSnapshotsClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def cache_stats( self, *, @@ -84,7 +84,7 @@ def cache_stats( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def clear_cache( self, *, diff --git a/elasticsearch/_sync/client/shutdown.py b/elasticsearch/_sync/client/shutdown.py index 28b360ca3..eb2b17661 100644 --- a/elasticsearch/_sync/client/shutdown.py +++ b/elasticsearch/_sync/client/shutdown.py @@ -20,12 +20,20 @@ from elastic_transport import ObjectApiResponse from ._base import NamespacedClient -from .utils import SKIP_IN_PATH, _quote, _rewrite_parameters +from .utils import ( + SKIP_IN_PATH, + Stability, + Visibility, + _availability_warning, + _quote, + _rewrite_parameters, +) class ShutdownClient(NamespacedClient): @_rewrite_parameters() + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) def delete_node( self, *, @@ -86,6 +94,7 @@ def delete_node( ) @_rewrite_parameters() + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) def get_node( self, *, @@ -144,6 +153,7 @@ def get_node( @_rewrite_parameters( body_fields=("reason", "type", "allocation_delay", "target_node_name"), ) + @_availability_warning(Stability.STABLE, Visibility.PRIVATE) def put_node( self, *, diff --git a/elasticsearch/_sync/client/simulate.py b/elasticsearch/_sync/client/simulate.py index d49a9a781..477b583d2 100644 --- a/elasticsearch/_sync/client/simulate.py +++ b/elasticsearch/_sync/client/simulate.py @@ -23,9 +23,9 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) @@ -40,7 +40,7 @@ class SimulateClient(NamespacedClient): "pipeline_substitutions", ), ) - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def ingest( self, *, diff --git a/elasticsearch/_sync/client/snapshot.py b/elasticsearch/_sync/client/snapshot.py index 00b7d8e38..30888d2ca 100644 --- a/elasticsearch/_sync/client/snapshot.py +++ b/elasticsearch/_sync/client/snapshot.py @@ -23,9 +23,9 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) @@ -955,7 +955,7 @@ def repository_analyze( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def repository_verify_integrity( self, *, diff --git a/elasticsearch/_sync/client/streams.py b/elasticsearch/_sync/client/streams.py index ba6ff1062..ed208cde8 100644 --- a/elasticsearch/_sync/client/streams.py +++ b/elasticsearch/_sync/client/streams.py @@ -23,15 +23,15 @@ from ._base import NamespacedClient from .utils import ( Stability, + _availability_warning, _rewrite_parameters, - _stability_warning, ) class StreamsClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def logs_disable( self, *, @@ -83,7 +83,7 @@ def logs_disable( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def logs_enable( self, *, @@ -138,7 +138,7 @@ def logs_enable( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def status( self, *, diff --git a/elasticsearch/_sync/client/tasks.py b/elasticsearch/_sync/client/tasks.py index d9fc0b385..811ac1731 100644 --- a/elasticsearch/_sync/client/tasks.py +++ b/elasticsearch/_sync/client/tasks.py @@ -23,16 +23,16 @@ from .utils import ( SKIP_IN_PATH, Stability, + _availability_warning, _quote, _rewrite_parameters, - _stability_warning, ) class TasksClient(NamespacedClient): @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def cancel( self, *, @@ -106,7 +106,7 @@ def cancel( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def get( self, *, @@ -164,7 +164,7 @@ def get( ) @_rewrite_parameters() - @_stability_warning(Stability.EXPERIMENTAL) + @_availability_warning(Stability.EXPERIMENTAL) def list( self, *, diff --git a/elasticsearch/_sync/client/utils.py b/elasticsearch/_sync/client/utils.py index 48ebcb217..afade6e2d 100644 --- a/elasticsearch/_sync/client/utils.py +++ b/elasticsearch/_sync/client/utils.py @@ -79,6 +79,12 @@ class Stability(Enum): EXPERIMENTAL = auto() +class Visibility(Enum): + PUBLIC = auto() + FEATURE_FLAG = auto() + PRIVATE = auto() + + _TYPE_HOSTS = Union[ str, Sequence[Union[str, Mapping[str, Union[str, int]], NodeConfig]] ] @@ -419,15 +425,23 @@ def wrapped(*args: Any, **kwargs: Any) -> Any: return wrapper -def _stability_warning( +def _availability_warning( stability: Stability, + visibility: Visibility = Visibility.PUBLIC, version: Optional[str] = None, message: Optional[str] = None, ) -> Callable[[F], F]: def wrapper(api: F) -> F: @wraps(api) def wrapped(*args: Any, **kwargs: Any) -> Any: - if stability == Stability.BETA: + if visibility == Visibility.PRIVATE: + warnings.warn( + "This API is private. " + "Private APIs are not subject to the support SLA of official GA features.", + category=GeneralAvailabilityWarning, + stacklevel=warn_stacklevel(), + ) + elif stability == Stability.BETA: warnings.warn( "This API is in beta and is subject to change. " "The design and code is less mature than official GA features and is being provided as-is with no warranties. " diff --git a/test_elasticsearch/test_client/test_utils.py b/test_elasticsearch/test_client/test_utils.py index e4713ff1c..714c727aa 100644 --- a/test_elasticsearch/test_client/test_utils.py +++ b/test_elasticsearch/test_client/test_utils.py @@ -20,7 +20,12 @@ import pytest -from elasticsearch._sync.client.utils import Stability, _quote, _stability_warning +from elasticsearch._sync.client.utils import ( + Stability, + Visibility, + _availability_warning, + _quote, +) from elasticsearch.exceptions import GeneralAvailabilityWarning @@ -43,10 +48,10 @@ def test_handles_unicode2(): assert "%E4%B8%AD*%E6%96%87," == _quote(string) -class TestStabilityWarning: +class TestAvailabilityWarning: def test_default(self): - @_stability_warning(stability=Stability.STABLE) + @_availability_warning(stability=Stability.STABLE) def func_default(*args, **kwargs): pass @@ -56,7 +61,7 @@ def func_default(*args, **kwargs): def test_beta(self, recwarn): - @_stability_warning(stability=Stability.BETA) + @_availability_warning(stability=Stability.BETA) def func_beta(*args, **kwargs): pass @@ -68,7 +73,7 @@ def func_beta(*args, **kwargs): def test_experimental(self, recwarn): - @_stability_warning(stability=Stability.EXPERIMENTAL) + @_availability_warning(stability=Stability.EXPERIMENTAL) def func_experimental(*args, **kwargs): pass @@ -77,3 +82,29 @@ def func_experimental(*args, **kwargs): match="This API is in technical preview and may be changed or removed in a future release.", ): func_experimental() + + def test_private(self, recwarn): + + @_availability_warning( + stability=Stability.STABLE, visibility=Visibility.PRIVATE + ) + def func_private(*args, **kwargs): + pass + + with pytest.warns( + GeneralAvailabilityWarning, + match="This API is private.", + ): + func_private() + + def test_private_and_beta(self, recwarn): + + @_availability_warning(stability=Stability.BETA, visibility=Visibility.PRIVATE) + def func_private_and_beta(*args, **kwargs): + pass + + with pytest.warns( + GeneralAvailabilityWarning, + match="This API is private.", + ): + func_private_and_beta() From 1ebeb867da5b2ba4fe7c2fe0c93bd1b27c17f2b0 Mon Sep 17 00:00:00 2001 From: Elastic Machine Date: Mon, 8 Dec 2025 11:54:31 +0100 Subject: [PATCH 32/36] Bumps stack to version 9.2.3-SNAPSHOT (#3222) --- .buildkite/pipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index bcd28805a..9d0b26b5f 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -5,7 +5,7 @@ steps: env: PYTHON_VERSION: "{{ matrix.python }}" TEST_SUITE: "platinum" - STACK_VERSION: "9.2.2-SNAPSHOT" + STACK_VERSION: "9.2.3-SNAPSHOT" PYTHON_CONNECTION_CLASS: "{{ matrix.connection }}" NOX_SESSION: "{{ matrix.nox_session }}" matrix: From 5517f41291958a5e1291cfbeb06b1dc3e174262f Mon Sep 17 00:00:00 2001 From: Elastic Machine Date: Mon, 15 Dec 2025 15:54:52 +0100 Subject: [PATCH 33/36] Auto-generated API code (#3208) --- elasticsearch/_async/client/__init__.py | 97 ++++--- elasticsearch/_async/client/async_search.py | 6 +- elasticsearch/_async/client/autoscaling.py | 6 +- elasticsearch/_async/client/cat.py | 38 +++ elasticsearch/_async/client/ccr.py | 20 +- elasticsearch/_async/client/cluster.py | 64 ++--- elasticsearch/_async/client/connector.py | 20 +- .../_async/client/dangling_indices.py | 20 +- elasticsearch/_async/client/enrich.py | 20 +- elasticsearch/_async/client/eql.py | 20 +- elasticsearch/_async/client/esql.py | 24 +- elasticsearch/_async/client/features.py | 8 +- elasticsearch/_async/client/fleet.py | 8 +- elasticsearch/_async/client/graph.py | 4 +- elasticsearch/_async/client/ilm.py | 36 +-- elasticsearch/_async/client/indices.py | 258 +++++++++--------- elasticsearch/_async/client/inference.py | 132 +++++---- elasticsearch/_async/client/ingest.py | 18 +- elasticsearch/_async/client/license.py | 12 +- elasticsearch/_async/client/logstash.py | 8 +- elasticsearch/_async/client/migration.py | 12 +- elasticsearch/_async/client/ml.py | 199 ++++++++------ elasticsearch/_async/client/monitoring.py | 4 +- elasticsearch/_async/client/nodes.py | 24 +- elasticsearch/_async/client/project.py | 3 +- elasticsearch/_async/client/query_rules.py | 32 +-- elasticsearch/_async/client/rollup.py | 24 +- .../_async/client/search_application.py | 16 +- .../_async/client/searchable_snapshots.py | 14 +- elasticsearch/_async/client/security.py | 15 +- elasticsearch/_async/client/shutdown.py | 7 +- elasticsearch/_async/client/simulate.py | 4 +- elasticsearch/_async/client/slm.py | 34 +-- elasticsearch/_async/client/snapshot.py | 36 +-- elasticsearch/_async/client/sql.py | 20 +- elasticsearch/_async/client/streams.py | 4 +- elasticsearch/_async/client/synonyms.py | 20 +- elasticsearch/_async/client/tasks.py | 8 +- elasticsearch/_async/client/text_structure.py | 22 +- elasticsearch/_async/client/transform.py | 63 ++++- elasticsearch/_async/client/watcher.py | 52 ++-- elasticsearch/_async/client/xpack.py | 11 +- elasticsearch/_sync/client/__init__.py | 97 ++++--- elasticsearch/_sync/client/async_search.py | 6 +- elasticsearch/_sync/client/autoscaling.py | 6 +- elasticsearch/_sync/client/cat.py | 38 +++ elasticsearch/_sync/client/ccr.py | 20 +- elasticsearch/_sync/client/cluster.py | 64 ++--- elasticsearch/_sync/client/connector.py | 20 +- .../_sync/client/dangling_indices.py | 20 +- elasticsearch/_sync/client/enrich.py | 20 +- elasticsearch/_sync/client/eql.py | 20 +- elasticsearch/_sync/client/esql.py | 24 +- elasticsearch/_sync/client/features.py | 8 +- elasticsearch/_sync/client/fleet.py | 8 +- elasticsearch/_sync/client/graph.py | 4 +- elasticsearch/_sync/client/ilm.py | 36 +-- elasticsearch/_sync/client/indices.py | 258 +++++++++--------- elasticsearch/_sync/client/inference.py | 132 +++++---- elasticsearch/_sync/client/ingest.py | 18 +- elasticsearch/_sync/client/license.py | 12 +- elasticsearch/_sync/client/logstash.py | 8 +- elasticsearch/_sync/client/migration.py | 12 +- elasticsearch/_sync/client/ml.py | 199 ++++++++------ elasticsearch/_sync/client/monitoring.py | 4 +- elasticsearch/_sync/client/nodes.py | 24 +- elasticsearch/_sync/client/project.py | 3 +- elasticsearch/_sync/client/query_rules.py | 32 +-- elasticsearch/_sync/client/rollup.py | 24 +- .../_sync/client/search_application.py | 16 +- .../_sync/client/searchable_snapshots.py | 14 +- elasticsearch/_sync/client/security.py | 15 +- elasticsearch/_sync/client/shutdown.py | 7 +- elasticsearch/_sync/client/simulate.py | 4 +- elasticsearch/_sync/client/slm.py | 34 +-- elasticsearch/_sync/client/snapshot.py | 36 +-- elasticsearch/_sync/client/sql.py | 20 +- elasticsearch/_sync/client/streams.py | 4 +- elasticsearch/_sync/client/synonyms.py | 20 +- elasticsearch/_sync/client/tasks.py | 8 +- elasticsearch/_sync/client/text_structure.py | 22 +- elasticsearch/_sync/client/transform.py | 63 ++++- elasticsearch/_sync/client/watcher.py | 52 ++-- elasticsearch/_sync/client/xpack.py | 11 +- elasticsearch/_version.py | 2 +- elasticsearch/dsl/aggs.py | 6 +- elasticsearch/dsl/field.py | 2 +- elasticsearch/dsl/types.py | 6 - 88 files changed, 1587 insertions(+), 1315 deletions(-) diff --git a/elasticsearch/_async/client/__init__.py b/elasticsearch/_async/client/__init__.py index 02d5b586c..60f487082 100644 --- a/elasticsearch/_async/client/__init__.py +++ b/elasticsearch/_async/client/__init__.py @@ -567,8 +567,8 @@ async def bulk( """ .. raw:: html -

Bulk index or delete documents. - Perform multiple index, create, delete, and update actions in a single request. +

Bulk index or delete documents.

+

Perform multiple index, create, delete, and update actions in a single request. This reduces overhead and can greatly increase indexing speed.

If the Elasticsearch security features are enabled, you must have the following index privileges for the target data stream, index, or index alias:

    @@ -616,6 +616,7 @@ async def bulk(
  • Perl: Check out Search::Elasticsearch::Client::5_0::Bulk and Search::Elasticsearch::Client::5_0::Scroll
  • Python: Check out elasticsearch.helpers.*
  • JavaScript: Check out client.helpers.*
  • +
  • Java: Check out co.elastic.clients.elasticsearch._helpers.bulk.BulkIngester
  • .NET: Check out BulkAllObservable
  • PHP: Check out bulk indexing.
  • Ruby: Check out Elasticsearch::Helpers::BulkHelper
  • @@ -773,8 +774,8 @@ async def clear_scroll( """ .. raw:: html -

    Clear a scrolling search. - Clear the search context and results for a scrolling search.

    +

    Clear a scrolling search.

    +

    Clear the search context and results for a scrolling search.

    ``_ @@ -827,8 +828,8 @@ async def close_point_in_time( """ .. raw:: html -

    Close a point in time. - A point in time must be opened explicitly before being used in search requests. +

    Close a point in time.

    +

    A point in time must be opened explicitly before being used in search requests. The keep_alive parameter tells Elasticsearch how long it should persist. A point in time is automatically closed when the keep_alive period has elapsed. However, keeping points in time has a cost; close them as soon as they are no longer required for search requests.

    @@ -905,8 +906,8 @@ async def count( """ .. raw:: html -

    Count search results. - Get the number of documents matching a query.

    +

    Count search results.

    +

    Get the number of documents matching a query.

    The query can be provided either by using a simple query string as a parameter, or by defining Query DSL within the request body. The query is optional. When no query is provided, the API uses match_all to count all the documents.

    The count API supports multi-target syntax. You can run a single count API search across multiple data streams and indices.

    @@ -1648,11 +1649,11 @@ async def delete_by_query_rethrottle( self, *, task_id: str, + requests_per_second: float, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, - requests_per_second: t.Optional[float] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html @@ -1670,9 +1671,13 @@ async def delete_by_query_rethrottle( """ if task_id in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'task_id'") + if requests_per_second is None: + raise ValueError("Empty value passed for parameter 'requests_per_second'") __path_parts: t.Dict[str, str] = {"task_id": _quote(task_id)} __path = f'/_delete_by_query/{__path_parts["task_id"]}/_rethrottle' __query: t.Dict[str, t.Any] = {} + if requests_per_second is not None: + __query["requests_per_second"] = requests_per_second if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -1681,8 +1686,6 @@ async def delete_by_query_rethrottle( __query["human"] = human if pretty is not None: __query["pretty"] = pretty - if requests_per_second is not None: - __query["requests_per_second"] = requests_per_second __headers = {"accept": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", @@ -1708,8 +1711,8 @@ async def delete_script( """ .. raw:: html -

    Delete a script or search template. - Deletes a stored script or search template.

    +

    Delete a script or search template.

    +

    Deletes a stored script or search template.

    ``_ @@ -2020,8 +2023,8 @@ async def explain( """ .. raw:: html -

    Explain a document match result. - Get information about why a specific document matches, or doesn't match, a query. +

    Explain a document match result.

    +

    Get information about why a specific document matches, or doesn't match, a query. It computes a score explanation for a query and a specific document.

    @@ -2437,8 +2440,8 @@ async def get_script( """ .. raw:: html -

    Get a script or search template. - Retrieves a stored script or search template.

    +

    Get a script or search template.

    +

    Retrieves a stored script or search template.

    ``_ @@ -2674,8 +2677,8 @@ async def health_report( """ .. raw:: html -

    Get the cluster health. - Get a report with the health status of an Elasticsearch cluster. +

    Get the cluster health.

    +

    Get a report with the health status of an Elasticsearch cluster. The report contains a list of indicators that compose Elasticsearch functionality.

    Each indicator has a health status of: green, unknown, yellow or red. The indicator will provide an explanation and metadata describing the reason for its current health status.

    @@ -2846,13 +2849,11 @@ async def index( "id": "elkbee" } } - - In this example, the operation will succeed since the supplied version of 2 is higher than the current document version of 1. - If the document was already updated and its version was set to 2 or higher, the indexing command will fail and result in a conflict (409 HTTP status code). - - A nice side effect is that there is no need to maintain strict ordering of async indexing operations run as a result of changes to a source database, as long as version numbers from the source database are used. - Even the simple case of updating the Elasticsearch index using data from a database is simplified if external versioning is used, as only the latest version will be used if the index operations arrive out of order. +

    In this example, the operation will succeed since the supplied version of 2 is higher than the current document version of 1. + If the document was already updated and its version was set to 2 or higher, the indexing command will fail and result in a conflict (409 HTTP status code).

    +

    A nice side effect is that there is no need to maintain strict ordering of async indexing operations run as a result of changes to a source database, as long as version numbers from the source database are used. + Even the simple case of updating the Elasticsearch index using data from a database is simplified if external versioning is used, as only the latest version will be used if the index operations arrive out of order.

    ``_ @@ -2987,8 +2988,8 @@ async def info( """ .. raw:: html -

    Get cluster info. - Get basic build, version, and cluster information. +

    Get cluster info.

    +

    Get basic build, version, and cluster information. ::: In Serverless, this API is retained for backward compatibility only. Some response fields, such as the version number, should be ignored.

    @@ -3704,8 +3705,8 @@ async def put_script( """ .. raw:: html -

    Create or update a script or search template. - Creates or updates a stored script or search template.

    +

    Create or update a script or search template.

    +

    Creates or updates a stored script or search template.

    ``_ @@ -4035,11 +4036,11 @@ async def reindex_rethrottle( self, *, task_id: str, + requests_per_second: float, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, - requests_per_second: t.Optional[float] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html @@ -4063,9 +4064,13 @@ async def reindex_rethrottle( """ if task_id in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'task_id'") + if requests_per_second is None: + raise ValueError("Empty value passed for parameter 'requests_per_second'") __path_parts: t.Dict[str, str] = {"task_id": _quote(task_id)} __path = f'/_reindex/{__path_parts["task_id"]}/_rethrottle' __query: t.Dict[str, t.Any] = {} + if requests_per_second is not None: + __query["requests_per_second"] = requests_per_second if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -4074,8 +4079,6 @@ async def reindex_rethrottle( __query["human"] = human if pretty is not None: __query["pretty"] = pretty - if requests_per_second is not None: - __query["requests_per_second"] = requests_per_second __headers = {"accept": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", @@ -5193,11 +5196,19 @@ async def search_mvt( ``_ - :param index: Comma-separated list of data streams, indices, or aliases to search - :param field: Field containing geospatial data to return - :param zoom: Zoom level for the vector tile to search - :param x: X coordinate for the vector tile to search - :param y: Y coordinate for the vector tile to search + :param index: A list of indices, data streams, or aliases to search. It supports + wildcards (`*`). To search all data streams and indices, omit this parameter + or use `*` or `_all`. To search a remote cluster, use the `:` + syntax. + :param field: A field that contains the geospatial data to return. It must be + a `geo_point` or `geo_shape` field. The field must have doc values enabled. + It cannot be a nested field. NOTE: Vector tiles do not natively support geometry + collections. For `geometrycollection` values in a `geo_shape` field, the + API returns a hits layer feature for each element of the collection. This + behavior may change in a future release. + :param zoom: The zoom level of the vector tile to search. It accepts `0` to `29`. + :param x: The X coordinate for the vector tile to search. + :param y: The Y coordinate for the vector tile to search. :param aggs: Sub-aggregations for the geotile_grid. It supports the following aggregation types: - `avg` - `boxplot` - `cardinality` - `extended stats` - `max` - `median absolute deviation` - `min` - `percentile` - `percentile-rank` @@ -6120,8 +6131,8 @@ async def update_by_query( """ .. raw:: html -

    Update documents. - Updates documents that match the specified query. +

    Update documents.

    +

    Updates documents that match the specified query. If no query is specified, performs an update on every document in the data stream or index without modifying the source, which is useful for picking up mapping changes.

    If the Elasticsearch security features are enabled, you must have the following index privileges for the target data stream, index, or alias:

      @@ -6400,11 +6411,11 @@ async def update_by_query_rethrottle( self, *, task_id: str, + requests_per_second: float, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, - requests_per_second: t.Optional[float] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html @@ -6422,9 +6433,13 @@ async def update_by_query_rethrottle( """ if task_id in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'task_id'") + if requests_per_second is None: + raise ValueError("Empty value passed for parameter 'requests_per_second'") __path_parts: t.Dict[str, str] = {"task_id": _quote(task_id)} __path = f'/_update_by_query/{__path_parts["task_id"]}/_rethrottle' __query: t.Dict[str, t.Any] = {} + if requests_per_second is not None: + __query["requests_per_second"] = requests_per_second if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -6433,8 +6448,6 @@ async def update_by_query_rethrottle( __query["human"] = human if pretty is not None: __query["pretty"] = pretty - if requests_per_second is not None: - __query["requests_per_second"] = requests_per_second __headers = {"accept": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", diff --git a/elasticsearch/_async/client/async_search.py b/elasticsearch/_async/client/async_search.py index 5ebe24cd0..eecbfb029 100644 --- a/elasticsearch/_async/client/async_search.py +++ b/elasticsearch/_async/client/async_search.py @@ -358,7 +358,7 @@ async def submit( :param allow_partial_search_results: Indicate if an error should be returned if there is a partial search failure or timeout :param analyze_wildcard: Specify whether wildcard and prefix queries should be - analyzed (default: false) + analyzed :param analyzer: The analyzer to use for the query string :param batched_reduce_size: Affects how often partial results become available, which happens whenever shard results are reduced. A partial reduction is @@ -374,7 +374,7 @@ async def submit( values for field names matching these patterns in the hits.fields property of the response. :param expand_wildcards: Whether to expand wildcard expression to concrete indices - that are open, closed or both. + that are open, closed or both :param explain: If true, returns detailed information about score computation as part of a hit. :param ext: Configuration of search extensions defined by Elasticsearch plugins. @@ -407,7 +407,7 @@ async def submit( you cannot specify an in the request path. :param post_filter: :param preference: Specify the node or shard the operation should be performed - on (default: random) + on :param profile: :param project_routing: Specifies a subset of projects to target for the search using project metadata tags in a subset of Lucene query syntax. Allowed Lucene diff --git a/elasticsearch/_async/client/autoscaling.py b/elasticsearch/_async/client/autoscaling.py index 50067cdde..e79f40084 100644 --- a/elasticsearch/_async/client/autoscaling.py +++ b/elasticsearch/_async/client/autoscaling.py @@ -54,7 +54,7 @@ async def delete_autoscaling_policy( ``_ - :param name: the name of the autoscaling policy + :param name: Name of the autoscaling policy :param master_timeout: Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. @@ -163,7 +163,7 @@ async def get_autoscaling_policy( ``_ - :param name: the name of the autoscaling policy + :param name: Name of the autoscaling policy :param master_timeout: Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. @@ -219,7 +219,7 @@ async def put_autoscaling_policy( ``_ - :param name: the name of the autoscaling policy + :param name: Name of the autoscaling policy :param policy: :param master_timeout: Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and diff --git a/elasticsearch/_async/client/cat.py b/elasticsearch/_async/client/cat.py index 02534d016..ebf9492cf 100644 --- a/elasticsearch/_async/client/cat.py +++ b/elasticsearch/_async/client/cat.py @@ -3308,10 +3308,20 @@ async def segments( self, *, index: t.Optional[t.Union[str, t.Sequence[str]]] = None, + allow_closed: t.Optional[bool] = None, + allow_no_indices: t.Optional[bool] = None, bytes: t.Optional[ t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] ] = None, error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Sequence[ + t.Union[str, t.Literal["all", "closed", "hidden", "none", "open"]] + ], + t.Union[str, t.Literal["all", "closed", "hidden", "none", "open"]], + ] + ] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, h: t.Optional[ @@ -3362,6 +3372,8 @@ async def segments( ] = None, help: t.Optional[bool] = None, human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, local: t.Optional[bool] = None, master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, @@ -3385,6 +3397,14 @@ async def segments( :param index: A comma-separated list of data streams, indices, and aliases used to limit the request. Supports wildcards (`*`). To target all data streams and indices, omit this parameter or use `*` or `_all`. + :param allow_closed: If true, allow closed indices to be returned in the response + otherwise if false, keep the legacy behaviour of throwing an exception if + index pattern matches closed indices + :param allow_no_indices: If false, the request returns an error if any wildcard + expression, index alias, or _all value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting foo*,bar* returns an error if an index starts + with foo but no index starts with bar. :param bytes: Sets the units for columns that contain a byte-size value. Note that byte-size value units work in terms of powers of 1024. For instance `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are @@ -3393,12 +3413,20 @@ async def segments( least `1.0`. If given, byte-size values are rendered as an integer with no suffix, representing the value of the column in the chosen unit. Values that are not an exact multiple of the chosen unit are rounded down. + :param expand_wildcards: Type of index that wildcard expressions can match. If + the request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values, such + as open,hidden. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple wildcards. :param help: When set to `true` will output available columns. This option can't be combined with any other query string option. + :param ignore_throttled: If true, concrete, expanded or aliased indices are ignored + when frozen. + :param ignore_unavailable: If true, missing or closed indices are not included + in the response. :param local: If `true`, the request computes the list of selected nodes from the local cluster state. If `false` the list of selected nodes are computed from the cluster state of the master node. In both cases the coordinating @@ -3423,10 +3451,16 @@ async def segments( __path_parts = {} __path = "/_cat/segments" __query: t.Dict[str, t.Any] = {} + if allow_closed is not None: + __query["allow_closed"] = allow_closed + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices if bytes is not None: __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards if filter_path is not None: __query["filter_path"] = filter_path if format is not None: @@ -3437,6 +3471,10 @@ async def segments( __query["help"] = help if human is not None: __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable if local is not None: __query["local"] = local if master_timeout is not None: diff --git a/elasticsearch/_async/client/ccr.py b/elasticsearch/_async/client/ccr.py index a98428ffb..f87ce10cf 100644 --- a/elasticsearch/_async/client/ccr.py +++ b/elasticsearch/_async/client/ccr.py @@ -125,8 +125,8 @@ async def follow( """ .. raw:: html -

      Create a follower. - Create a cross-cluster replication follower index that follows a specific leader index. +

      Create a follower.

      +

      Create a cross-cluster replication follower index that follows a specific leader index. When the API returns, the follower index exists and cross-cluster replication starts replicating operations from the leader index to the follower index.

      @@ -368,8 +368,8 @@ async def forget_follower( """ .. raw:: html -

      Forget a follower. - Remove the cross-cluster replication follower retention leases from the leader.

      +

      Forget a follower.

      +

      Remove the cross-cluster replication follower retention leases from the leader.

      A following index takes out retention leases on its leader index. These leases are used to increase the likelihood that the shards of the leader index retain the history of operations that the shards of the following index need to run replication. When a follower index is converted to a regular index by the unfollow API (either by directly calling the API or by index lifecycle management tasks), these leases are removed. @@ -382,7 +382,7 @@ async def forget_follower( ``_ - :param index: the name of the leader index for which specified follower retention + :param index: Name of the leader index for which specified follower retention leases should be removed :param follower_cluster: :param follower_index: @@ -640,8 +640,8 @@ async def put_auto_follow_pattern( """ .. raw:: html -

      Create or update auto-follow patterns. - Create a collection of cross-cluster replication auto-follow patterns for a remote cluster. +

      Create or update auto-follow patterns.

      +

      Create a collection of cross-cluster replication auto-follow patterns for a remote cluster. Newly created indices on the remote cluster that match any of the patterns are automatically configured as follower indices. Indices on the remote cluster that were created before the auto-follow pattern was created will not be auto-followed even if they match the pattern.

      This API can also be used to update auto-follow patterns. @@ -853,8 +853,8 @@ async def resume_follow( """ .. raw:: html -

      Resume a follower. - Resume a cross-cluster replication follower index that was paused. +

      Resume a follower.

      +

      Resume a cross-cluster replication follower index that was paused. The follower index could have been paused with the pause follower API. Alternatively it could be paused due to replication that cannot be retried due to failures during following tasks. When this API returns, the follower index will resume fetching operations from the leader index.

      @@ -862,7 +862,7 @@ async def resume_follow( ``_ - :param index: The name of the follow index to resume following. + :param index: Name of the follow index to resume following :param master_timeout: Period to wait for a connection to the master node. :param max_outstanding_read_requests: :param max_outstanding_write_requests: diff --git a/elasticsearch/_async/client/cluster.py b/elasticsearch/_async/client/cluster.py index a6efa8529..8c4e77856 100644 --- a/elasticsearch/_async/client/cluster.py +++ b/elasticsearch/_async/client/cluster.py @@ -47,8 +47,8 @@ async def allocation_explain( """ .. raw:: html -

      Explain the shard allocations. - Get explanations for shard allocations in the cluster. +

      Explain the shard allocations.

      +

      Get explanations for shard allocations in the cluster. This API accepts the current_node, index, primary and shard parameters in the request body or in query parameters, but not in both at the same time. For unassigned shards, it provides an explanation for why the shard is unassigned. For assigned shards, it provides an explanation for why the shard is remaining on its current node and has not moved or rebalanced to another node. @@ -127,8 +127,8 @@ async def delete_component_template( """ .. raw:: html -

      Delete component templates. - Component templates are building blocks for constructing index templates that specify index mappings, settings, and aliases.

      +

      Delete component templates.

      +

      Component templates are building blocks for constructing index templates that specify index mappings, settings, and aliases.

      ``_ @@ -182,8 +182,8 @@ async def delete_voting_config_exclusions( """ .. raw:: html -

      Clear cluster voting config exclusions. - Remove master-eligible nodes from the voting configuration exclusion list.

      +

      Clear cluster voting config exclusions.

      +

      Remove master-eligible nodes from the voting configuration exclusion list.

      ``_ @@ -236,8 +236,8 @@ async def exists_component_template( """ .. raw:: html -

      Check component templates. - Returns information about whether a particular component template exists.

      +

      Check component templates.

      +

      Returns information about whether a particular component template exists.

      ``_ @@ -296,8 +296,8 @@ async def get_component_template( """ .. raw:: html -

      Get component templates. - Get information about component templates.

      +

      Get component templates.

      +

      Get information about component templates.

      ``_ @@ -306,7 +306,7 @@ async def get_component_template( request. Wildcard (`*`) expressions are supported. :param flat_settings: If `true`, returns settings in flat format. :param include_defaults: Return all default configurations for the component - template (default: false) + template :param local: If `true`, the request retrieves information from the local node only. If `false`, information is retrieved from the master node. :param master_timeout: Period to wait for a connection to the master node. If @@ -572,8 +572,8 @@ async def info( """ .. raw:: html -

      Get cluster info. - Returns basic information about the cluster.

      +

      Get cluster info.

      +

      Returns basic information about the cluster.

      ``_ @@ -618,8 +618,8 @@ async def pending_tasks( """ .. raw:: html -

      Get the pending cluster tasks. - Get information about cluster-level changes (such as create index, update mapping, allocate or fail shard) that have not yet taken effect.

      +

      Get the pending cluster tasks.

      +

      Get information about cluster-level changes (such as create index, update mapping, allocate or fail shard) that have not yet taken effect.

      NOTE: This API returns a list of any pending updates to the cluster state. These are distinct from the tasks reported by the task management API which include periodic tasks and tasks initiated by the user, such as node stats, search queries, or create index requests. However, if a user-initiated task such as a create index command causes a cluster state update, the activity of this task might be reported by both task api and pending cluster tasks API.

      @@ -674,8 +674,8 @@ async def post_voting_config_exclusions( """ .. raw:: html -

      Update voting configuration exclusions. - Update the cluster voting config exclusions by node IDs or node names. +

      Update voting configuration exclusions.

      +

      Update the cluster voting config exclusions by node IDs or node names. By default, if there are more than three master-eligible nodes in the cluster and you remove fewer than half of the master-eligible nodes in the cluster at once, the voting configuration automatically shrinks. If you want to shrink the voting configuration to contain fewer than three nodes or to remove half or more of the master-eligible nodes in the cluster at once, use this API to remove departing nodes from the voting configuration manually. The API adds an entry for each specified node to the cluster’s voting configuration exclusions list. @@ -757,8 +757,8 @@ async def put_component_template( """ .. raw:: html -

      Create or update a component template. - Component templates are building blocks for constructing index templates that specify index mappings, settings, and aliases.

      +

      Create or update a component template.

      +

      Component templates are building blocks for constructing index templates that specify index mappings, settings, and aliases.

      An index template can be composed of multiple component templates. To use a component template, specify it in an index template’s composed_of list. Component templates are only applied to new data streams and indices as part of a matching index template.

      @@ -883,10 +883,10 @@ async def put_settings( ``_ - :param flat_settings: Return settings in flat format (default: false) - :param master_timeout: Explicit operation timeout for connection to master node + :param flat_settings: Return settings in flat format + :param master_timeout: The period to wait for a connection to the master node. :param persistent: The settings that persist after the cluster restarts. - :param timeout: Explicit operation timeout + :param timeout: The period to wait for a response. :param transient: The settings that do not persist after the cluster restarts. """ __path_parts: t.Dict[str, str] = {} @@ -992,8 +992,8 @@ async def reroute( """ .. raw:: html -

      Reroute the cluster. - Manually change the allocation of individual shards in the cluster. +

      Reroute the cluster.

      +

      Manually change the allocation of individual shards in the cluster. For example, a shard can be moved from one node to another explicitly, an allocation can be canceled, and an unassigned shard can be explicitly allocated to a specific node.

      It is important to note that after processing any reroute commands Elasticsearch will perform rebalancing as normal (respecting the values of settings such as cluster.routing.rebalance.enable) in order to remain in a balanced state. For example, if the requested allocation includes moving a shard from node1 to node2 then this may cause a shard to be moved from node2 back to node1 to even things out.

      @@ -1093,8 +1093,8 @@ async def state( """ .. raw:: html -

      Get the cluster state. - Get comprehensive information about the state of the cluster.

      +

      Get the cluster state.

      +

      Get comprehensive information about the state of the cluster.

      The cluster state is an internal data structure which keeps track of a variety of information needed by every node, including the identity and attributes of the other nodes in the cluster; cluster-wide settings; index metadata, including the mapping and settings for each index; the location and status of every shard copy in the cluster.

      The elected master node ensures that every node in the cluster has a copy of the same cluster state. This API lets you retrieve a representation of this internal state for debugging or diagnostic purposes. @@ -1111,19 +1111,19 @@ async def state( ``_ - :param metric: Limit the information returned to the specified metrics + :param metric: Limit the information returned to the specified metrics. :param index: A comma-separated list of index names; use `_all` or empty string to perform the operation on all indices :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified) :param expand_wildcards: Whether to expand wildcard expression to concrete indices - that are open, closed or both. - :param flat_settings: Return settings in flat format (default: false) + that are open, closed or both + :param flat_settings: Return settings in flat format :param ignore_unavailable: Whether specified concrete indices should be ignored when unavailable (missing or closed) :param local: Return local information, do not retrieve the state from master - node (default: false) + node :param master_timeout: Timeout for waiting for new cluster state in case it is blocked :param wait_for_metadata_version: Wait for the metadata version to be equal or @@ -1194,8 +1194,8 @@ async def stats( """ .. raw:: html -

      Get cluster statistics. - Get basic index metrics (shard numbers, store size, memory usage) and information about the current nodes that form the cluster (number, roles, os, jvm versions, memory usage, cpu and installed plugins).

      +

      Get cluster statistics.

      +

      Get basic index metrics (shard numbers, store size, memory usage) and information about the current nodes that form the cluster (number, roles, os, jvm versions, memory usage, cpu and installed plugins).

      ``_ diff --git a/elasticsearch/_async/client/connector.py b/elasticsearch/_async/client/connector.py index 797371850..19a4c382b 100644 --- a/elasticsearch/_async/client/connector.py +++ b/elasticsearch/_async/client/connector.py @@ -644,8 +644,8 @@ async def sync_job_check_in( """ .. raw:: html -

      Check in a connector sync job. - Check in a connector sync job and set the last_seen field to the current time before updating it in the internal index.

      +

      Check in a connector sync job.

      +

      Check in a connector sync job and set the last_seen field to the current time before updating it in the internal index.

      To sync data using self-managed connectors, you need to deploy the Elastic connector service on your own infrastructure. This service runs automatically on Elastic Cloud for Elastic managed connectors.

      @@ -701,8 +701,8 @@ async def sync_job_claim( """ .. raw:: html -

      Claim a connector sync job. - This action updates the job status to in_progress and sets the last_seen and started_at timestamps to the current time. +

      Claim a connector sync job.

      +

      This action updates the job status to in_progress and sets the last_seen and started_at timestamps to the current time. Additionally, it can set the sync_cursor property for the sync job.

      This API is not intended for direct connector management by users. It supports the implementation of services that utilize the connector protocol to communicate with Elasticsearch.

      @@ -820,8 +820,8 @@ async def sync_job_error( """ .. raw:: html -

      Set a connector sync job error. - Set the error field for a connector sync job and set its status to error.

      +

      Set a connector sync job error.

      +

      Set the error field for a connector sync job and set its status to error.

      To sync data using self-managed connectors, you need to deploy the Elastic connector service on your own infrastructure. This service runs automatically on Elastic Cloud for Elastic managed connectors.

      @@ -1087,8 +1087,8 @@ async def sync_job_update_stats( """ .. raw:: html -

      Set the connector sync job stats. - Stats include: deleted_document_count, indexed_document_count, indexed_document_volume, and total_document_count. +

      Set the connector sync job stats.

      +

      Stats include: deleted_document_count, indexed_document_count, indexed_document_volume, and total_document_count. You can also update last_seen. This API is mainly used by the connector service for updating sync job information.

      To sync data using self-managed connectors, you need to deploy the Elastic connector service on your own infrastructure. @@ -1403,8 +1403,8 @@ async def update_features( """ .. raw:: html -

      Update the connector features. - Update the connector features in the connector document. +

      Update the connector features.

      +

      Update the connector features in the connector document. This API can be used to control the following aspects of a connector:

      • document-level security
      • diff --git a/elasticsearch/_async/client/dangling_indices.py b/elasticsearch/_async/client/dangling_indices.py index 57289c91a..cc69bb046 100644 --- a/elasticsearch/_async/client/dangling_indices.py +++ b/elasticsearch/_async/client/dangling_indices.py @@ -30,7 +30,7 @@ async def delete_dangling_index( self, *, index_uuid: str, - accept_data_loss: bool, + accept_data_loss: t.Optional[bool] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, @@ -41,8 +41,8 @@ async def delete_dangling_index( """ .. raw:: html -

        Delete a dangling index. - If Elasticsearch encounters index data that is absent from the current cluster state, those indices are considered to be dangling. +

        Delete a dangling index.

        +

        If Elasticsearch encounters index data that is absent from the current cluster state, those indices are considered to be dangling. For example, this can happen if you delete more than cluster.indices.tombstones.size indices while an Elasticsearch node is offline.

        @@ -52,13 +52,11 @@ async def delete_dangling_index( API to find the UUID. :param accept_data_loss: This parameter must be set to true to acknowledge that it will no longer be possible to recove data from the dangling index. - :param master_timeout: Specify timeout for connection to master - :param timeout: Explicit operation timeout + :param master_timeout: The period to wait for a connection to the master node. + :param timeout: The period to wait for a response. """ if index_uuid in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'index_uuid'") - if accept_data_loss is None: - raise ValueError("Empty value passed for parameter 'accept_data_loss'") __path_parts: t.Dict[str, str] = {"index_uuid": _quote(index_uuid)} __path = f'/_dangling/{__path_parts["index_uuid"]}' __query: t.Dict[str, t.Any] = {} @@ -91,7 +89,7 @@ async def import_dangling_index( self, *, index_uuid: str, - accept_data_loss: bool, + accept_data_loss: t.Optional[bool] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, @@ -116,13 +114,11 @@ async def import_dangling_index( from or determine which shard copies are fresh and which are stale, it cannot guarantee that the imported data represents the latest state of the index when it was last in the cluster. - :param master_timeout: Specify timeout for connection to master - :param timeout: Explicit operation timeout + :param master_timeout: The period to wait for a connection to the master node. + :param timeout: The period to wait for a response. """ if index_uuid in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'index_uuid'") - if accept_data_loss is None: - raise ValueError("Empty value passed for parameter 'accept_data_loss'") __path_parts: t.Dict[str, str] = {"index_uuid": _quote(index_uuid)} __path = f'/_dangling/{__path_parts["index_uuid"]}' __query: t.Dict[str, t.Any] = {} diff --git a/elasticsearch/_async/client/enrich.py b/elasticsearch/_async/client/enrich.py index ae37254ca..19dd27f14 100644 --- a/elasticsearch/_async/client/enrich.py +++ b/elasticsearch/_async/client/enrich.py @@ -39,8 +39,8 @@ async def delete_policy( """ .. raw:: html -

        Delete an enrich policy. - Deletes an existing enrich policy and its enrich index.

        +

        Delete an enrich policy.

        +

        Deletes an existing enrich policy and its enrich index.

        ``_ @@ -88,8 +88,8 @@ async def execute_policy( """ .. raw:: html -

        Run an enrich policy. - Create the enrich index for an existing enrich policy.

        +

        Run an enrich policy.

        +

        Create the enrich index for an existing enrich policy.

        ``_ @@ -140,8 +140,8 @@ async def get_policy( """ .. raw:: html -

        Get an enrich policy. - Returns information about an enrich policy.

        +

        Get an enrich policy.

        +

        Returns information about an enrich policy.

        ``_ @@ -198,8 +198,8 @@ async def put_policy( """ .. raw:: html -

        Create an enrich policy. - Creates an enrich policy.

        +

        Create an enrich policy.

        +

        Creates an enrich policy.

        ``_ @@ -259,8 +259,8 @@ async def stats( """ .. raw:: html -

        Get enrich stats. - Returns enrich coordinator statistics and information about enrich policies that are currently executing.

        +

        Get enrich stats.

        +

        Returns enrich coordinator statistics and information about enrich policies that are currently executing.

        ``_ diff --git a/elasticsearch/_async/client/eql.py b/elasticsearch/_async/client/eql.py index 66e70037c..d4a70b75f 100644 --- a/elasticsearch/_async/client/eql.py +++ b/elasticsearch/_async/client/eql.py @@ -38,8 +38,8 @@ async def delete( """ .. raw:: html -

        Delete an async EQL search. - Delete an async EQL search or a stored synchronous EQL search. +

        Delete an async EQL search.

        +

        Delete an async EQL search or a stored synchronous EQL search. The API also deletes results for the search.

        @@ -89,8 +89,8 @@ async def get( """ .. raw:: html -

        Get async EQL search results. - Get the current status and available results for an async EQL search or a stored synchronous EQL search.

        +

        Get async EQL search results.

        +

        Get the current status and available results for an async EQL search or a stored synchronous EQL search.

        ``_ @@ -143,8 +143,8 @@ async def get_status( """ .. raw:: html -

        Get the async EQL status. - Get the current status for an async EQL search or a stored synchronous EQL search without returning results.

        +

        Get the async EQL status.

        +

        Get the current status for an async EQL search or a stored synchronous EQL search without returning results.

        ``_ @@ -243,14 +243,14 @@ async def search( """ .. raw:: html -

        Get EQL search results. - Returns search results for an Event Query Language (EQL) query. +

        Get EQL search results.

        +

        Returns search results for an Event Query Language (EQL) query. EQL assumes each document in a data stream or index corresponds to an event.

        ``_ - :param index: The name of the index to scope the operation + :param index: Comma-separated list of index names to scope the operation :param query: EQL query you wish to run. :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices @@ -296,7 +296,7 @@ async def search( Defaults to 10 :param tiebreaker_field: Field used to sort hits with the same timestamp in ascending order - :param timestamp_field: Field containing event timestamp. Default "@timestamp" + :param timestamp_field: Field containing event timestamp. :param wait_for_completion_timeout: """ if index in SKIP_IN_PATH: diff --git a/elasticsearch/_async/client/esql.py b/elasticsearch/_async/client/esql.py index 8d75f809c..11325a129 100644 --- a/elasticsearch/_async/client/esql.py +++ b/elasticsearch/_async/client/esql.py @@ -90,8 +90,8 @@ async def async_query( """ .. raw:: html -

        Run an async ES|QL query. - Asynchronously run an ES|QL (Elasticsearch query language) query, monitor its progress, and retrieve results when they become available.

        +

        Run an async ES|QL query.

        +

        Asynchronously run an ES|QL (Elasticsearch query language) query, monitor its progress, and retrieve results when they become available.

        The API accepts the same parameters and request body as the synchronous query API, along with additional async related properties.

        @@ -226,8 +226,8 @@ async def async_query_delete( """ .. raw:: html -

        Delete an async ES|QL query. - If the query is still running, it is cancelled. +

        Delete an async ES|QL query.

        +

        If the query is still running, it is cancelled. Otherwise, the stored results are deleted.

        If the Elasticsearch security features are enabled, only the following users can use this API to delete a query:

          @@ -292,8 +292,8 @@ async def async_query_get( """ .. raw:: html -

          Get async ES|QL query results. - Get the current status and available results or stored results for an ES|QL asynchronous query. +

          Get async ES|QL query results.

          +

          Get the current status and available results or stored results for an ES|QL asynchronous query. If the Elasticsearch security features are enabled, only the user who first submitted the ES|QL query can retrieve the results using this API.

          @@ -417,8 +417,8 @@ async def get_query( """ .. raw:: html -

          Get a specific running ES|QL query information. - Returns an object extended information about a running ES|QL query.

          +

          Get a specific running ES|QL query information.

          +

          Returns an object extended information about a running ES|QL query.

          ``_ @@ -461,8 +461,8 @@ async def list_queries( """ .. raw:: html -

          Get running ES|QL queries information. - Returns an object containing IDs and other information about the running ES|QL queries.

          +

          Get running ES|QL queries information.

          +

          Returns an object containing IDs and other information about the running ES|QL queries.

          ``_ @@ -543,8 +543,8 @@ async def query( """ .. raw:: html -

          Run an ES|QL query. - Get search results for an ES|QL (Elasticsearch query language) query.

          +

          Run an ES|QL query.

          +

          Get search results for an ES|QL (Elasticsearch query language) query.

          ``_ diff --git a/elasticsearch/_async/client/features.py b/elasticsearch/_async/client/features.py index 09421cda8..7dec9cf73 100644 --- a/elasticsearch/_async/client/features.py +++ b/elasticsearch/_async/client/features.py @@ -38,8 +38,8 @@ async def get_features( """ .. raw:: html -

          Get the features. - Get a list of features that can be included in snapshots using the feature_states field when creating a snapshot. +

          Get the features.

          +

          Get a list of features that can be included in snapshots using the feature_states field when creating a snapshot. You can use this API to determine which feature states to include when taking a snapshot. By default, all feature states are included in a snapshot if that snapshot includes the global state, or none if it does not.

          A feature state includes one or more system indices necessary for a given feature to function. @@ -89,8 +89,8 @@ async def reset_features( """ .. raw:: html -

          Reset the features. - Clear all of the state information stored in system indices by Elasticsearch features, including the security and machine learning indices.

          +

          Reset the features.

          +

          Clear all of the state information stored in system indices by Elasticsearch features, including the security and machine learning indices.

          WARNING: Intended for development and testing use only. Do not reset features on a production cluster.

          Return a cluster to the same state as a new installation by resetting the feature state for all Elasticsearch features. This deletes all state information stored in system indices.

          diff --git a/elasticsearch/_async/client/fleet.py b/elasticsearch/_async/client/fleet.py index 915803884..27d68fb41 100644 --- a/elasticsearch/_async/client/fleet.py +++ b/elasticsearch/_async/client/fleet.py @@ -138,8 +138,8 @@ async def msearch( """ .. raw:: html -

          Run multiple Fleet searches. - Run several Fleet searches with a single API request. +

          Run multiple Fleet searches.

          +

          Run several Fleet searches with a single API request. The API follows the same structure as the multi search API. However, similar to the Fleet search API, it supports the wait_for_checkpoints parameter.

          @@ -388,8 +388,8 @@ async def search( """ .. raw:: html -

          Run a Fleet search. - The purpose of the Fleet search API is to provide an API where the search will be run only +

          Run a Fleet search.

          +

          The purpose of the Fleet search API is to provide an API where the search will be run only after the provided checkpoint has been processed and is visible for searches inside of Elasticsearch.

          diff --git a/elasticsearch/_async/client/graph.py b/elasticsearch/_async/client/graph.py index d239cf4ae..ad4ce8a80 100644 --- a/elasticsearch/_async/client/graph.py +++ b/elasticsearch/_async/client/graph.py @@ -47,8 +47,8 @@ async def explore( """ .. raw:: html -

          Explore graph analytics. - Extract and summarize information about the documents and terms in an Elasticsearch data stream or index. +

          Explore graph analytics.

          +

          Extract and summarize information about the documents and terms in an Elasticsearch data stream or index. The easiest way to understand the behavior of this API is to use the Graph UI to explore connections. An initial request to the _explore API contains a seed query that identifies the documents of interest and specifies the fields that define the vertices and connections you want to include in the graph. Subsequent requests enable you to spider out from one more vertices of interest. diff --git a/elasticsearch/_async/client/ilm.py b/elasticsearch/_async/client/ilm.py index acc7f14b8..e0784f194 100644 --- a/elasticsearch/_async/client/ilm.py +++ b/elasticsearch/_async/client/ilm.py @@ -40,8 +40,8 @@ async def delete_lifecycle( """ .. raw:: html -

          Delete a lifecycle policy. - You cannot delete policies that are currently in use. If the policy is being used to manage any indices, the request fails and returns an error.

          +

          Delete a lifecycle policy.

          +

          You cannot delete policies that are currently in use. If the policy is being used to manage any indices, the request fails and returns an error.

          ``_ @@ -96,8 +96,8 @@ async def explain_lifecycle( """ .. raw:: html -

          Explain the lifecycle state. - Get the current lifecycle status for one or more indices. +

          Explain the lifecycle state.

          +

          Get the current lifecycle status for one or more indices. For data streams, the API retrieves the current lifecycle status for the stream's backing indices.

          The response indicates when the index entered each lifecycle state, provides the definition of the running phase, and information about any failures.

          @@ -260,8 +260,8 @@ async def migrate_to_data_tiers( """ .. raw:: html -

          Migrate to data tiers routing. - Switch the indices, ILM policies, and legacy, composable, and component templates from using custom node attributes and attribute-based allocation filters to using data tiers. +

          Migrate to data tiers routing.

          +

          Switch the indices, ILM policies, and legacy, composable, and component templates from using custom node attributes and attribute-based allocation filters to using data tiers. Optionally, delete one legacy index template. Using node roles enables ILM to automatically move the indices between data tiers.

          Migrating away from custom node attributes routing can be manually performed. @@ -341,8 +341,8 @@ async def move_to_step( """ .. raw:: html -

          Move to a lifecycle step. - Manually move an index into a specific step in the lifecycle policy and run that step.

          +

          Move to a lifecycle step.

          +

          Manually move an index into a specific step in the lifecycle policy and run that step.

          WARNING: This operation can result in the loss of data. Manually moving an index into a specific step runs that step even if it has already been performed. This is a potentially destructive action and this should be considered an expert level API.

          You must specify both the current step and the step to be executed in the body of the request. The request will fail if the current step does not match the step currently running for the index @@ -413,8 +413,8 @@ async def put_lifecycle( """ .. raw:: html -

          Create or update a lifecycle policy. - If the specified policy exists, it is replaced and the policy version is incremented.

          +

          Create or update a lifecycle policy.

          +

          If the specified policy exists, it is replaced and the policy version is incremented.

          NOTE: Only the latest version of the policy is stored, you cannot revert to previous versions.

          @@ -473,8 +473,8 @@ async def remove_policy( """ .. raw:: html -

          Remove policies from an index. - Remove the assigned lifecycle policies from an index or a data stream's backing indices. +

          Remove policies from an index.

          +

          Remove the assigned lifecycle policies from an index or a data stream's backing indices. It also stops managing the indices.

          @@ -518,8 +518,8 @@ async def retry( """ .. raw:: html -

          Retry a policy. - Retry running the lifecycle policy for an index that is in the ERROR step. +

          Retry a policy.

          +

          Retry running the lifecycle policy for an index that is in the ERROR step. The API sets the policy back to the step where the error occurred and runs the step. Use the explain lifecycle state API to determine whether an index is in the ERROR step.

          @@ -566,8 +566,8 @@ async def start( """ .. raw:: html -

          Start the ILM plugin. - Start the index lifecycle management plugin if it is currently stopped. +

          Start the ILM plugin.

          +

          Start the index lifecycle management plugin if it is currently stopped. ILM is started automatically when the cluster is formed. Restarting ILM is necessary only when it has been stopped using the stop ILM API.

          @@ -619,8 +619,8 @@ async def stop( """ .. raw:: html -

          Stop the ILM plugin. - Halt all lifecycle management operations and stop the index lifecycle management plugin. +

          Stop the ILM plugin.

          +

          Halt all lifecycle management operations and stop the index lifecycle management plugin. This is useful when you are performing maintenance on the cluster and need to prevent ILM from performing any actions on your indices.

          The API returns as soon as the stop request has been acknowledged, but the plugin might continue to run until in-progress operations complete and the plugin can be safely stopped. Use the get ILM status API to check whether ILM is running.

          diff --git a/elasticsearch/_async/client/indices.py b/elasticsearch/_async/client/indices.py index 9421ff940..f623e2006 100644 --- a/elasticsearch/_async/client/indices.py +++ b/elasticsearch/_async/client/indices.py @@ -165,8 +165,8 @@ async def analyze( """ .. raw:: html -

          Get tokens from text analysis. - The analyze API performs analysis on a text string and returns the resulting tokens.

          +

          Get tokens from text analysis.

          +

          The analyze API performs analysis on a text string and returns the resulting tokens.

          Generating excessive amount of tokens may cause a node to run out of memory. The index.analyze.max_token_count setting enables you to limit the number of tokens that can be produced. If more than this limit of tokens gets generated, an error occurs. @@ -244,7 +244,6 @@ async def analyze( ) @_rewrite_parameters() - @_availability_warning(Stability.EXPERIMENTAL) async def cancel_migrate_reindex( self, *, @@ -315,8 +314,8 @@ async def clear_cache( """ .. raw:: html -

          Clear the cache. - Clear the cache of one or more indices. +

          Clear the cache.

          +

          Clear the cache of one or more indices. For data streams, the API clears the caches of the stream's backing indices.

          By default, the clear cache API clears all caches. To clear only specific caches, use the fielddata, query, or request parameters. @@ -408,8 +407,8 @@ async def clone( """ .. raw:: html -

          Clone an index. - Clone an existing index into a new index. +

          Clone an index.

          +

          Clone an existing index into a new index. Each original primary shard is cloned into a new primary shard in the new index.

          IMPORTANT: Elasticsearch does not apply index templates to the resulting index. The API also does not copy index metadata from the original index. @@ -533,8 +532,8 @@ async def close( """ .. raw:: html -

          Close an index. - A closed index is blocked for read or write operations and does not allow all operations that opened indices allow. +

          Close an index.

          +

          A closed index is blocked for read or write operations and does not allow all operations that opened indices allow. It is not possible to index documents or to search for documents in a closed index. Closed indices do not have to maintain internal data structures for indexing or searching documents, which results in a smaller overhead on the cluster.

          When opening or closing an index, the master node is responsible for restarting the index shards to reflect the new state of the index. @@ -630,8 +629,8 @@ async def create( """ .. raw:: html -

          Create an index. - You can use the create index API to add a new index to an Elasticsearch cluster. +

          Create an index.

          +

          You can use the create index API to add a new index to an Elasticsearch cluster. When creating an index, you can specify the following:

          • Settings for the index.
          • @@ -778,7 +777,6 @@ async def create_data_stream( @_rewrite_parameters( body_name="create_from", ) - @_availability_warning(Stability.EXPERIMENTAL) async def create_from( self, *, @@ -926,8 +924,8 @@ async def delete( """ .. raw:: html -

            Delete indices. - Deleting an index deletes its documents, shards, and metadata. +

            Delete indices.

            +

            Deleting an index deletes its documents, shards, and metadata. It does not delete related Kibana components, such as data views, visualizations, or dashboards.

            You cannot delete the current write index of a data stream. To delete the index, you must roll over the data stream so a new write index is created. @@ -1004,8 +1002,8 @@ async def delete_alias( """ .. raw:: html -

            Delete an alias. - Removes a data stream or index from an alias.

            +

            Delete an alias.

            +

            Removes a data stream or index from an alias.

            ``_ @@ -1072,18 +1070,18 @@ async def delete_data_lifecycle( """ .. raw:: html -

            Delete data stream lifecycles. - Removes the data stream lifecycle from a data stream, rendering it not managed by the data stream lifecycle.

            +

            Delete data stream lifecycles.

            +

            Removes the data stream lifecycle from a data stream, rendering it not managed by the data stream lifecycle.

            ``_ :param name: A comma-separated list of data streams of which the data stream - lifecycle will be deleted; use `*` to get all data streams + lifecycle will be deleted. Use `*` to get all data streams :param expand_wildcards: Whether wildcard expressions should get expanded to open or closed indices (default: open) - :param master_timeout: Specify timeout for connection to master - :param timeout: Explicit timestamp for the document + :param master_timeout: The period to wait for a connection to the master node. + :param timeout: The period to wait for a response. """ if name in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'name'") @@ -1136,8 +1134,8 @@ async def delete_data_stream( """ .. raw:: html -

            Delete data streams. - Deletes one or more data streams and their backing indices.

            +

            Delete data streams.

            +

            Deletes one or more data streams and their backing indices.

            ``_ @@ -1200,18 +1198,18 @@ async def delete_data_stream_options( """ .. raw:: html -

            Delete data stream options. - Removes the data stream options from a data stream.

            +

            Delete data stream options.

            +

            Removes the data stream options from a data stream.

            ``_ :param name: A comma-separated list of data streams of which the data stream - options will be deleted; use `*` to get all data streams + options will be deleted. Use `*` to get all data streams :param expand_wildcards: Whether wildcard expressions should get expanded to - open or closed indices (default: open) - :param master_timeout: Specify timeout for connection to master - :param timeout: Explicit timestamp for the document + open or closed indices + :param master_timeout: The period to wait for a connection to the master node. + :param timeout: The period to wait for a response. """ if name in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'name'") @@ -1257,8 +1255,8 @@ async def delete_index_template( """ .. raw:: html -

            Delete an index template. - The provided may contain multiple template names separated by a comma. If multiple template +

            Delete an index template.

            +

            The provided may contain multiple template names separated by a comma. If multiple template names are specified then there is no wildcard support and the provided names should match completely with existing templates.

            @@ -1315,8 +1313,8 @@ async def delete_template( """ .. raw:: html -

            Delete a legacy index template. - IMPORTANT: This documentation is about legacy index templates, which are deprecated and will be replaced by the composable templates introduced in Elasticsearch 7.8.

            +

            Delete a legacy index template.

            +

            IMPORTANT: This documentation is about legacy index templates, which are deprecated and will be replaced by the composable templates introduced in Elasticsearch 7.8.

            ``_ @@ -1382,8 +1380,8 @@ async def disk_usage( """ .. raw:: html -

            Analyze the index disk usage. - Analyze the disk usage of each field of an index or data stream. +

            Analyze the index disk usage.

            +

            Analyze the disk usage of each field of an index or data stream. This API might not support indices created in previous Elasticsearch versions. The result of a small index can be inaccurate as some parts of an index might not be analyzed by the API.

            NOTE: The total size of fields of the analyzed shards of the index in the response is usually smaller than the index store_size value because some small metadata files are ignored and some parts of data files might not be scanned by the API. @@ -1466,8 +1464,8 @@ async def downsample( """ .. raw:: html -

            Downsample an index. - Aggregate a time series (TSDS) index and store pre-computed statistical summaries (min, max, sum, value_count and avg) for each metric field grouped by a configured time interval. +

            Downsample an index.

            +

            Aggregate a time series (TSDS) index and store pre-computed statistical summaries (min, max, sum, value_count and avg) for each metric field grouped by a configured time interval. For example, a TSDS index that contains metrics sampled every 10 seconds can be downsampled to an hourly index. All documents within an hour interval are summarized and stored as a single document in the downsample index.

            NOTE: Only indices in a time series data stream are supported. @@ -1543,8 +1541,8 @@ async def exists( """ .. raw:: html -

            Check indices. - Check if one or more indices, index aliases, or data streams exist.

            +

            Check indices.

            +

            Check if one or more indices, index aliases, or data streams exist.

            ``_ @@ -1763,8 +1761,8 @@ async def exists_template( """ .. raw:: html -

            Check existence of index templates. - Get information about whether index templates exist. +

            Check existence of index templates.

            +

            Get information about whether index templates exist. Index templates define settings, mappings, and aliases that can be applied automatically to new indices.

            IMPORTANT: This documentation is about legacy index templates, which are deprecated and will be replaced by the composable templates introduced in Elasticsearch 7.8.

            @@ -1823,16 +1821,16 @@ async def explain_data_lifecycle( """ .. raw:: html -

            Get the status for a data stream lifecycle. - Get information about an index or data stream's current data stream lifecycle status, such as time since index creation, time since rollover, the lifecycle configuration managing the index, or any errors encountered during lifecycle execution.

            +

            Get the status for a data stream lifecycle.

            +

            Get information about an index or data stream's current data stream lifecycle status, such as time since index creation, time since rollover, the lifecycle configuration managing the index, or any errors encountered during lifecycle execution.

            ``_ - :param index: The name of the index to explain - :param include_defaults: indicates if the API should return the default values + :param index: Comma-separated list of index names to explain + :param include_defaults: Indicates if the API should return the default values the system uses for the index's lifecycle - :param master_timeout: Specify timeout for connection to master + :param master_timeout: The period to wait for a connection to the master node. """ if index in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'index'") @@ -1886,8 +1884,8 @@ async def field_usage_stats( """ .. raw:: html -

            Get field usage stats. - Get field usage information for each shard and field of an index. +

            Get field usage stats.

            +

            Get field usage information for each shard and field of an index. Field usage statistics are automatically captured when queries are running on a cluster. A shard-level search request that accesses a given field, even if multiple times during that request, is counted as a single use.

            The response body reports the per-shard usage count of the data structures that back the fields in the index. @@ -1968,8 +1966,8 @@ async def flush( """ .. raw:: html -

            Flush data streams or indices. - Flushing a data stream or index is the process of making sure that any data that is currently only stored in the transaction log is also permanently stored in the Lucene index. +

            Flush data streams or indices.

            +

            Flushing a data stream or index is the process of making sure that any data that is currently only stored in the transaction log is also permanently stored in the Lucene index. When restarting, Elasticsearch replays any unflushed operations from the transaction log into the Lucene index to bring it back into the state that it was in before the restart. Elasticsearch automatically triggers flushes as needed, using heuristics that trade off the size of the unflushed transaction log against the cost of performing each flush.

            After each operation has been flushed it is permanently stored in the Lucene index. @@ -2062,8 +2060,8 @@ async def forcemerge( """ .. raw:: html -

            Force a merge. - Perform the force merge operation on the shards of one or more indices. +

            Force a merge.

            +

            Perform the force merge operation on the shards of one or more indices. For data streams, the API forces a merge on the shards of the stream's backing indices.

            Merging reduces the number of segments in each shard by merging some of them together and also frees up the space used by deleted documents. Merging normally happens automatically, but sometimes it is useful to trigger a merge manually.

            @@ -2114,15 +2112,15 @@ async def forcemerge( :param expand_wildcards: Whether to expand wildcard expression to concrete indices that are open, closed or both. :param flush: Specify whether the index should be flushed after performing the - operation (default: true) + operation :param ignore_unavailable: Whether specified concrete indices should be ignored when unavailable (missing or closed) :param max_num_segments: The number of segments the index should be merged into - (default: dynamic) + (defayult: dynamic) :param only_expunge_deletes: Specify whether the operation should only expunge deleted documents :param wait_for_completion: Should the request wait until the force merge is - completed. + completed """ __path_parts: t.Dict[str, str] if index not in SKIP_IN_PATH: @@ -2197,8 +2195,8 @@ async def get( """ .. raw:: html -

            Get index information. - Get information about one or more indices. For data streams, the API returns information about the +

            Get index information.

            +

            Get information about one or more indices. For data streams, the API returns information about the stream’s backing indices.

            @@ -2291,8 +2289,8 @@ async def get_alias( """ .. raw:: html -

            Get aliases. - Retrieves information for one or more data stream or index aliases.

            +

            Get aliases.

            +

            Retrieves information for one or more data stream or index aliases.

            ``_ @@ -2435,8 +2433,8 @@ async def get_data_lifecycle_stats( """ .. raw:: html -

            Get data stream lifecycle stats. - Get statistics about the data streams that are managed by a data stream lifecycle.

            +

            Get data stream lifecycle stats.

            +

            Get statistics about the data streams that are managed by a data stream lifecycle.

            ``_ @@ -2730,8 +2728,8 @@ async def get_field_mapping( """ .. raw:: html -

            Get mapping definitions. - Retrieves mapping definitions for one or more fields. +

            Get mapping definitions.

            +

            Retrieves mapping definitions for one or more fields. For data streams, the API retrieves field mappings for the stream’s backing indices.

            This API is useful if you don't need a complete mapping or if an index mapping contains a large number of fields.

            @@ -2809,14 +2807,14 @@ async def get_index_template( """ .. raw:: html -

            Get index templates. - Get information about one or more index templates.

            +

            Get index templates.

            +

            Get information about one or more index templates.

            ``_ - :param name: Comma-separated list of index template names used to limit the request. - Wildcard (*) expressions are supported. + :param name: Name of index template to retrieve. Wildcard (*) expressions are + supported. :param flat_settings: If true, returns settings in flat format. :param include_defaults: If true, returns all relevant default configurations for the index template. @@ -2886,8 +2884,8 @@ async def get_mapping( """ .. raw:: html -

            Get mapping definitions. - For data streams, the API retrieves mappings for the stream’s backing indices.

            +

            Get mapping definitions.

            +

            For data streams, the API retrieves mappings for the stream’s backing indices.

            ``_ @@ -2947,7 +2945,6 @@ async def get_mapping( ) @_rewrite_parameters() - @_availability_warning(Stability.EXPERIMENTAL) async def get_migrate_reindex_status( self, *, @@ -3019,8 +3016,8 @@ async def get_settings( """ .. raw:: html -

            Get index settings. - Get setting information for one or more indices. +

            Get index settings.

            +

            Get setting information for one or more indices. For data streams, it returns setting information for the stream's backing indices.

            @@ -3111,8 +3108,8 @@ async def get_template( """ .. raw:: html -

            Get legacy index templates. - Get information about one or more index templates.

            +

            Get legacy index templates.

            +

            Get information about one or more index templates.

            IMPORTANT: This documentation is about legacy index templates, which are deprecated and will be replaced by the composable templates introduced in Elasticsearch 7.8.

            @@ -3163,7 +3160,6 @@ async def get_template( @_rewrite_parameters( body_name="reindex", ) - @_availability_warning(Stability.EXPERIMENTAL) async def migrate_reindex( self, *, @@ -3231,8 +3227,8 @@ async def migrate_to_data_stream( """ .. raw:: html -

            Convert an index alias to a data stream. - Converts an index alias to a data stream. +

            Convert an index alias to a data stream.

            +

            Converts an index alias to a data stream. You must have a matching index template that is data stream enabled. The alias must meet the following criteria: The alias must have a write index; @@ -3296,8 +3292,8 @@ async def modify_data_stream( """ .. raw:: html -

            Update data streams. - Performs one or more data stream modification actions in a single atomic operation.

            +

            Update data streams.

            +

            Performs one or more data stream modification actions in a single atomic operation.

            ``_ @@ -3360,8 +3356,8 @@ async def open( """ .. raw:: html -

            Open a closed index. - For data streams, the API opens any closed backing indices.

            +

            Open a closed index.

            +

            For data streams, the API opens any closed backing indices.

            A closed index is blocked for read/write operations and does not allow all operations that opened indices allow. It is not possible to index documents or to search for documents in a closed index. This allows closed indices to not have to maintain internal data structures for indexing or searching documents, resulting in a smaller overhead on the cluster.

            @@ -3454,8 +3450,8 @@ async def promote_data_stream( """ .. raw:: html -

            Promote a data stream. - Promote a data stream from a replicated data stream managed by cross-cluster replication (CCR) to a regular data stream.

            +

            Promote a data stream.

            +

            Promote a data stream from a replicated data stream managed by cross-cluster replication (CCR) to a regular data stream.

            With CCR auto following, a data stream from a remote cluster can be replicated to the local cluster. These data streams can't be rolled over in the local cluster. These replicated data streams roll over only if the upstream data stream rolls over. @@ -3467,7 +3463,7 @@ async def promote_data_stream( ``_ - :param name: The name of the data stream + :param name: The name of the data stream to promote :param master_timeout: Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. @@ -3527,8 +3523,8 @@ async def put_alias( """ .. raw:: html -

            Create or update an alias. - Adds a data stream or index to an alias.

            +

            Create or update an alias.

            +

            Adds a data stream or index to an alias.

            ``_ @@ -3613,7 +3609,7 @@ async def put_data_lifecycle( *, name: t.Union[str, t.Sequence[str]], data_retention: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, - downsampling: t.Optional[t.Mapping[str, t.Any]] = None, + downsampling: t.Optional[t.Sequence[t.Mapping[str, t.Any]]] = None, enabled: t.Optional[bool] = None, error_trace: t.Optional[bool] = None, expand_wildcards: t.Optional[ @@ -3634,8 +3630,8 @@ async def put_data_lifecycle( """ .. raw:: html -

            Update data stream lifecycles. - Update the data stream lifecycle of the specified data streams.

            +

            Update data stream lifecycles.

            +

            Update the data stream lifecycle of the specified data streams.

            ``_ @@ -3801,8 +3797,8 @@ async def put_data_stream_options( """ .. raw:: html -

            Update data stream options. - Update the data stream options of the specified data streams.

            +

            Update data stream options.

            +

            Update the data stream options of the specified data streams.

            ``_ @@ -3972,8 +3968,8 @@ async def put_index_template( """ .. raw:: html -

            Create or update an index template. - Index templates define settings, mappings, and aliases that can be applied automatically to new indices.

            +

            Create or update an index template.

            +

            Index templates define settings, mappings, and aliases that can be applied automatically to new indices.

            Elasticsearch applies templates to new indices based on an wildcard pattern that matches the index name. Index templates are applied during data stream or index creation. For data streams, these settings and mappings are applied when the stream's backing indices are created. @@ -4004,7 +4000,7 @@ async def put_index_template( via `actions.auto_create_index`. If set to `false`, then indices or data streams matching the template must always be explicitly created, and may never be automatically created. - :param cause: User defined reason for creating/updating the index template + :param cause: User defined reason for creating or updating the index template :param composed_of: An ordered list of component template names. Component templates are merged in the order specified, meaning that the last component template specified has the highest precedence. @@ -4156,8 +4152,8 @@ async def put_mapping( """ .. raw:: html -

            Update field mappings. - Add new fields to an existing data stream or index. +

            Update field mappings.

            +

            Add new fields to an existing data stream or index. You can use the update mapping API to:

            • Add a new field to an existing index
            • @@ -4174,7 +4170,7 @@ async def put_mapping( ``_ :param index: A comma-separated list of index names the mapping should be added - to (supports wildcards); use `_all` or omit to add the mapping on all indices. + to (supports wildcards). Use `_all` or omit to add the mapping on all indices. :param allow_no_indices: If `false`, the request returns an error if any wildcard expression, index alias, or `_all` value targets only missing or closed indices. This behavior applies even if the request targets other open indices. @@ -4301,8 +4297,8 @@ async def put_settings( """ .. raw:: html -

              Update index settings. - Changes dynamic index settings in real time. +

              Update index settings.

              +

              Changes dynamic index settings in real time. For data streams, index setting changes are applied to all backing indices by default.

              To revert a setting to the default value, use a null value. The list of per-index settings that can be updated dynamically on live indices can be found in index settings documentation. @@ -4455,8 +4451,8 @@ async def put_template( """ .. raw:: html -

              Create or update a legacy index template. - Index templates define settings, mappings, and aliases that can be applied automatically to new indices. +

              Create or update a legacy index template.

              +

              Index templates define settings, mappings, and aliases that can be applied automatically to new indices. Elasticsearch applies templates to new indices based on an index pattern that matches the index name.

              IMPORTANT: This documentation is about legacy index templates, which are deprecated and will be replaced by the composable templates introduced in Elasticsearch 7.8.

              Composable templates always take precedence over legacy templates. @@ -4476,7 +4472,7 @@ async def put_template( :param name: The name of the template :param aliases: Aliases for the index. - :param cause: User defined reason for creating/updating the index template + :param cause: User defined reason for creating or updating the index template :param create: If true, this request cannot replace or update existing index templates. :param index_patterns: Array of wildcard expressions used to match the names @@ -4563,8 +4559,8 @@ async def recovery( """ .. raw:: html -

              Get index recovery information. - Get information about ongoing and completed shard recoveries for one or more indices. +

              Get index recovery information.

              +

              Get information about ongoing and completed shard recoveries for one or more indices. For data streams, the API returns information for the stream's backing indices.

              All recoveries, whether ongoing or complete, are kept in the cluster state and may be reported on at any time.

              Shard recovery is the process of initializing a shard copy, such as restoring a primary shard from a snapshot or creating a replica shard from a primary shard. @@ -4661,8 +4657,8 @@ async def refresh( """ .. raw:: html -

              Refresh an index. - A refresh makes recent operations performed on one or more indices available for search. +

              Refresh an index.

              +

              A refresh makes recent operations performed on one or more indices available for search. For data streams, the API runs the refresh operation on the stream’s backing indices.

              By default, Elasticsearch periodically refreshes indices every second, but only on indices that have received one search request or more in the last 30 seconds. You can change this default interval with the index.refresh_interval setting.

              @@ -4745,8 +4741,8 @@ async def reload_search_analyzers( """ .. raw:: html -

              Reload search analyzers. - Reload an index's search analyzers and their resources. +

              Reload search analyzers.

              +

              Reload an index's search analyzers and their resources. For data streams, the API reloads search analyzers and resources for the stream's backing indices.

              IMPORTANT: After reloading the search analyzers you should clear the request cache to make sure it doesn't contain responses derived from the previous versions of the analyzer.

              You can use the reload search analyzers API to pick up changes to synonym files used in the synonym_graph or synonym token filter of a search analyzer. @@ -5071,8 +5067,8 @@ async def resolve_index( """ .. raw:: html -

              Resolve indices. - Resolve the names and/or index patterns for indices, aliases, and data streams. +

              Resolve indices.

              +

              Resolve the names and/or index patterns for indices, aliases, and data streams. Multiple patterns and remote clusters are supported.

              @@ -5160,8 +5156,8 @@ async def rollover( """ .. raw:: html -

              Roll over to a new index. - TIP: We recommend using the index lifecycle rollover action to automate rollovers. However, Serverless does not support Index Lifecycle Management (ILM), so don't use this approach in the Serverless context.

              +

              Roll over to a new index.

              +

              TIP: We recommend using the index lifecycle rollover action to automate rollovers. However, Serverless does not support Index Lifecycle Management (ILM), so don't use this approach in the Serverless context.

              The rollover API creates a new index for a data stream or index alias. The API behavior depends on the rollover target.

              Roll over a data stream

              @@ -5297,8 +5293,8 @@ async def segments( """ .. raw:: html -

              Get index segments. - Get low-level information about the Lucene segments in index shards. +

              Get index segments.

              +

              Get low-level information about the Lucene segments in index shards. For data streams, the API returns information about the stream's backing indices.

              @@ -5378,8 +5374,8 @@ async def shard_stores( """ .. raw:: html -

              Get index shard stores. - Get store information about replica shards in one or more indices. +

              Get index shard stores.

              +

              Get store information about replica shards in one or more indices. For data streams, the API retrieves store information for the stream's backing indices.

              The index shard stores API returns the following information:

                @@ -5462,8 +5458,8 @@ async def shrink( """ .. raw:: html -

                Shrink an index. - Shrink an index into a new index with fewer primary shards.

                +

                Shrink an index.

                +

                Shrink an index into a new index with fewer primary shards.

                Before you can shrink an index:

                • The index must be read-only.
                • @@ -5570,8 +5566,8 @@ async def simulate_index_template( """ .. raw:: html -

                  Simulate an index. - Get the index configuration that would be applied to the specified index from an existing index template.

                  +

                  Simulate an index.

                  +

                  Get the index configuration that would be applied to the specified index from an existing index template.

                  ``_ @@ -5669,8 +5665,8 @@ async def simulate_template( """ .. raw:: html -

                  Simulate an index template. - Get the index configuration that would be applied by a particular index template.

                  +

                  Simulate an index template.

                  +

                  Get the index configuration that would be applied by a particular index template.

                  ``_ @@ -5808,8 +5804,8 @@ async def split( """ .. raw:: html -

                  Split an index. - Split an index into a new index with more primary shards.

                  +

                  Split an index.

                  +

                  Split an index into a new index with more primary shards.

                  • Before you can split an index:

                    @@ -5933,8 +5929,8 @@ async def stats( """ .. raw:: html -

                    Get index statistics. - For data streams, the API retrieves statistics for the stream's backing indices.

                    +

                    Get index statistics.

                    +

                    For data streams, the API retrieves statistics for the stream's backing indices.

                    By default, the returned statistics are index-level with primaries and total aggregations. primaries are the values for only the primary shards. total are the accumulated values for both primary and replica shards.

                    @@ -5947,7 +5943,7 @@ async def stats( :param index: A comma-separated list of index names; use `_all` or empty string to perform the operation on all indices - :param metric: Limit the information returned the specific metrics. + :param metric: Limit the information returned the specific metrics :param completion_fields: Comma-separated list or wildcard expressions of fields to include in fielddata and suggest statistics. :param expand_wildcards: Type of index that wildcard patterns can match. If the @@ -6038,8 +6034,8 @@ async def update_aliases( """ .. raw:: html -

                    Create or update an alias. - Adds a data stream or index to an alias.

                    +

                    Create or update an alias.

                    +

                    Adds a data stream or index to an alias.

                    ``_ @@ -6117,8 +6113,8 @@ async def validate_query( """ .. raw:: html -

                    Validate a query. - Validates a query without running it.

                    +

                    Validate a query.

                    +

                    Validates a query without running it.

                    ``_ diff --git a/elasticsearch/_async/client/inference.py b/elasticsearch/_async/client/inference.py index 934395f17..793e913a7 100644 --- a/elasticsearch/_async/client/inference.py +++ b/elasticsearch/_async/client/inference.py @@ -44,14 +44,16 @@ async def completion( """ .. raw:: html -

                    Perform completion inference on the service

                    +

                    Perform completion inference on the service.

                    ``_ :param inference_id: The inference Id :param input: Inference input. Either a string or an array of strings. - :param task_settings: Optional task settings + :param task_settings: Task settings for the individual inference request. These + settings are specific to the you specified and override the task + settings specified when initializing the service. :param timeout: Specifies the amount of time to wait for the inference request to complete. """ @@ -116,15 +118,17 @@ async def delete( """ .. raw:: html -

                    Delete an inference endpoint

                    +

                    Delete an inference endpoint.

                    +

                    This API requires the manage_inference cluster privilege (the built-in inference_admin role grants this privilege).

                    ``_ :param inference_id: The inference identifier. :param task_type: The task type - :param dry_run: When true, the endpoint is not deleted and a list of ingest processors - which reference this endpoint is returned. + :param dry_run: When true, checks the semantic_text fields and inference processors + that reference the endpoint and returns them in a list, but does not delete + the endpoint. :param force: When true, the inference endpoint is forcefully deleted even if it is still being used by ingest processors or semantic text fields. """ @@ -190,7 +194,8 @@ async def get( """ .. raw:: html -

                    Get an inference endpoint

                    +

                    Get an inference endpoint.

                    +

                    This API requires the monitor_inference cluster privilege (the built-in inference_admin and inference_user roles grant this privilege).

                    ``_ @@ -544,7 +549,7 @@ async def put_alibabacloud( self, *, task_type: t.Union[ - str, t.Literal["completion", "rerank", "space_embedding", "text_embedding"] + str, t.Literal["completion", "rerank", "sparse_embedding", "text_embedding"] ], alibabacloud_inference_id: str, service: t.Optional[t.Union[str, t.Literal["alibabacloud-ai-search"]]] = None, @@ -573,7 +578,9 @@ async def put_alibabacloud( this case, `alibabacloud-ai-search`. :param service_settings: Settings used to install the inference model. These settings are specific to the `alibabacloud-ai-search` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `sparse_embedding` or `text_embedding` task types. Not applicable to + the `rerank` or `completion` task types. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -669,7 +676,8 @@ async def put_amazonbedrock( this case, `amazonbedrock`. :param service_settings: Settings used to install the inference model. These settings are specific to the `amazonbedrock` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `completion` task type. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -771,7 +779,9 @@ async def put_amazonsagemaker( :param service_settings: Settings used to install the inference model. These settings are specific to the `amazon_sagemaker` service and `service_settings.api` you specified. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `sparse_embedding` or `text_embedding` task types. Not applicable to + the `rerank`, `completion`, or `chat_completion` task types. :param task_settings: Settings to configure the inference task. These settings are specific to the task type and `service_settings.api` you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -825,12 +835,7 @@ async def put_amazonsagemaker( ) @_rewrite_parameters( - body_fields=( - "service", - "service_settings", - "chunking_settings", - "task_settings", - ), + body_fields=("service", "service_settings", "task_settings"), ) async def put_anthropic( self, @@ -839,7 +844,6 @@ async def put_anthropic( anthropic_inference_id: str, service: t.Optional[t.Union[str, t.Literal["anthropic"]]] = None, service_settings: t.Optional[t.Mapping[str, t.Any]] = None, - chunking_settings: t.Optional[t.Mapping[str, t.Any]] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, @@ -863,8 +867,7 @@ async def put_anthropic( :param service: The type of service supported for the specified task type. In this case, `anthropic`. :param service_settings: Settings used to install the inference model. These - settings are specific to the `watsonxai` service. - :param chunking_settings: The chunking configuration object. + settings are specific to the `anthropic` service. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -902,8 +905,6 @@ async def put_anthropic( __body["service"] = service if service_settings is not None: __body["service_settings"] = service_settings - if chunking_settings is not None: - __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings __headers = {"accept": "application/json", "content-type": "application/json"} @@ -955,8 +956,10 @@ async def put_azureaistudio( :param service: The type of service supported for the specified task type. In this case, `azureaistudio`. :param service_settings: Settings used to install the inference model. These - settings are specific to the `openai` service. - :param chunking_settings: The chunking configuration object. + settings are specific to the `azureaistudio` service. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `rerank` or `completion` + task types. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -1056,7 +1059,8 @@ async def put_azureopenai( this case, `azureopenai`. :param service_settings: Settings used to install the inference model. These settings are specific to the `azureopenai` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `completion` task type. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -1148,7 +1152,9 @@ async def put_cohere( this case, `cohere`. :param service_settings: Settings used to install the inference model. These settings are specific to the `cohere` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `rerank` or `completion` + task type. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -1200,12 +1206,7 @@ async def put_cohere( ) @_rewrite_parameters( - body_fields=( - "service", - "service_settings", - "chunking_settings", - "task_settings", - ), + body_fields=("service", "service_settings", "task_settings"), ) async def put_contextualai( self, @@ -1214,7 +1215,6 @@ async def put_contextualai( contextualai_inference_id: str, service: t.Optional[t.Union[str, t.Literal["contextualai"]]] = None, service_settings: t.Optional[t.Mapping[str, t.Any]] = None, - chunking_settings: t.Optional[t.Mapping[str, t.Any]] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, @@ -1239,7 +1239,6 @@ async def put_contextualai( this case, `contextualai`. :param service_settings: Settings used to install the inference model. These settings are specific to the `contextualai` service. - :param chunking_settings: The chunking configuration object. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -1277,8 +1276,6 @@ async def put_contextualai( __body["service"] = service if service_settings is not None: __body["service_settings"] = service_settings - if chunking_settings is not None: - __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings __headers = {"accept": "application/json", "content-type": "application/json"} @@ -1372,7 +1369,9 @@ async def put_custom( this case, `custom`. :param service_settings: Settings used to install the inference model. These settings are specific to the `custom` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `sparse_embedding` or `text_embedding` task types. Not applicable to + the `rerank` or `completion` task types. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. """ @@ -1420,7 +1419,7 @@ async def put_custom( ) @_rewrite_parameters( - body_fields=("service", "service_settings", "chunking_settings"), + body_fields=("service", "service_settings"), ) async def put_deepseek( self, @@ -1429,7 +1428,6 @@ async def put_deepseek( deepseek_inference_id: str, service: t.Optional[t.Union[str, t.Literal["deepseek"]]] = None, service_settings: t.Optional[t.Mapping[str, t.Any]] = None, - chunking_settings: t.Optional[t.Mapping[str, t.Any]] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, @@ -1452,7 +1450,6 @@ async def put_deepseek( this case, `deepseek`. :param service_settings: Settings used to install the inference model. These settings are specific to the `deepseek` service. - :param chunking_settings: The chunking configuration object. :param timeout: Specifies the amount of time to wait for the inference endpoint to be created. """ @@ -1486,8 +1483,6 @@ async def put_deepseek( __body["service"] = service if service_settings is not None: __body["service_settings"] = service_settings - if chunking_settings is not None: - __body["chunking_settings"] = chunking_settings __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", @@ -1554,7 +1549,9 @@ async def put_elasticsearch( this case, `elasticsearch`. :param service_settings: Settings used to install the inference model. These settings are specific to the `elasticsearch` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `sparse_embedding` and `text_embedding` task types. Not applicable to + the `rerank` task type. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -1735,7 +1732,8 @@ async def put_googleaistudio( this case, `googleaistudio`. :param service_settings: Settings used to install the inference model. These settings are specific to the `googleaistudio` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `completion` task type. :param timeout: Specifies the amount of time to wait for the inference endpoint to be created. """ @@ -1825,7 +1823,9 @@ async def put_googlevertexai( this case, `googlevertexai`. :param service_settings: Settings used to install the inference model. These settings are specific to the `googlevertexai` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `rerank`, `completion`, + or `chat_completion` task types. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -1953,7 +1953,9 @@ async def put_hugging_face( this case, `hugging_face`. :param service_settings: Settings used to install the inference model. These settings are specific to the `hugging_face` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `rerank`, `completion`, + or `chat_completion` task types. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -2047,7 +2049,8 @@ async def put_jinaai( this case, `jinaai`. :param service_settings: Settings used to install the inference model. These settings are specific to the `jinaai` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `rerank` task type. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -2133,7 +2136,9 @@ async def put_llama( this case, `llama`. :param service_settings: Settings used to install the inference model. These settings are specific to the `llama` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `completion` or `chat_completion` + task types. :param timeout: Specifies the amount of time to wait for the inference endpoint to be created. """ @@ -2215,7 +2220,9 @@ async def put_mistral( this case, `mistral`. :param service_settings: Settings used to install the inference model. These settings are specific to the `mistral` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `completion` or `chat_completion` + task types. :param timeout: Specifies the amount of time to wait for the inference endpoint to be created. """ @@ -2305,7 +2312,9 @@ async def put_openai( this case, `openai`. :param service_settings: Settings used to install the inference model. These settings are specific to the `openai` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `completion` or `chat_completion` + task types. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -2396,7 +2405,8 @@ async def put_voyageai( this case, `voyageai`. :param service_settings: Settings used to install the inference model. These settings are specific to the `voyageai` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `rerank` task type. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -2448,7 +2458,7 @@ async def put_voyageai( ) @_rewrite_parameters( - body_fields=("service", "service_settings"), + body_fields=("service", "service_settings", "chunking_settings"), ) async def put_watsonx( self, @@ -2459,6 +2469,7 @@ async def put_watsonx( watsonx_inference_id: str, service: t.Optional[t.Union[str, t.Literal["watsonxai"]]] = None, service_settings: t.Optional[t.Mapping[str, t.Any]] = None, + chunking_settings: t.Optional[t.Mapping[str, t.Any]] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, @@ -2483,6 +2494,9 @@ async def put_watsonx( this case, `watsonxai`. :param service_settings: Settings used to install the inference model. These settings are specific to the `watsonxai` service. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `completion` or `chat_completion` + task types. :param timeout: Specifies the amount of time to wait for the inference endpoint to be created. """ @@ -2516,6 +2530,8 @@ async def put_watsonx( __body["service"] = service if service_settings is not None: __body["service_settings"] = service_settings + if chunking_settings is not None: + __body["chunking_settings"] = chunking_settings __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", @@ -2547,7 +2563,7 @@ async def rerank( """ .. raw:: html -

                    Perform reranking inference on the service

                    +

                    Perform reranking inference on the service.

                    ``_ @@ -2619,14 +2635,16 @@ async def sparse_embedding( """ .. raw:: html -

                    Perform sparse embedding inference on the service

                    +

                    Perform sparse embedding inference on the service.

                    ``_ :param inference_id: The inference Id :param input: Inference input. Either a string or an array of strings. - :param task_settings: Optional task settings + :param task_settings: Task settings for the individual inference request. These + settings are specific to the you specified and override the task + settings specified when initializing the service. :param timeout: Specifies the amount of time to wait for the inference request to complete. """ @@ -2684,7 +2702,7 @@ async def text_embedding( """ .. raw:: html -

                    Perform text embedding inference on the service

                    +

                    Perform text embedding inference on the service.

                    ``_ @@ -2698,7 +2716,9 @@ async def text_embedding( to the relevant service-specific documentation for more info. > info > The `input_type` parameter specified on the root level of the request body will take precedence over the `input_type` parameter specified in `task_settings`. - :param task_settings: Optional task settings + :param task_settings: Task settings for the individual inference request. These + settings are specific to the you specified and override the task + settings specified when initializing the service. :param timeout: Specifies the amount of time to wait for the inference request to complete. """ diff --git a/elasticsearch/_async/client/ingest.py b/elasticsearch/_async/client/ingest.py index 336169a90..3bb9827c3 100644 --- a/elasticsearch/_async/client/ingest.py +++ b/elasticsearch/_async/client/ingest.py @@ -151,8 +151,8 @@ async def delete_pipeline( """ .. raw:: html -

                    Delete pipelines. - Delete one or more ingest pipelines.

                    +

                    Delete pipelines.

                    +

                    Delete one or more ingest pipelines.

                    ``_ @@ -204,8 +204,8 @@ async def geo_ip_stats( """ .. raw:: html -

                    Get GeoIP statistics. - Get download statistics for GeoIP2 databases that are used with the GeoIP processor.

                    +

                    Get GeoIP statistics.

                    +

                    Get download statistics for GeoIP2 databases that are used with the GeoIP processor.

                    ``_ @@ -355,7 +355,7 @@ async def get_pipeline( :param master_timeout: Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. - :param summary: Return pipelines without their definitions (default: false) + :param summary: Return pipelines without their definitions """ __path_parts: t.Dict[str, str] if id not in SKIP_IN_PATH: @@ -399,8 +399,8 @@ async def processor_grok( """ .. raw:: html -

                    Run a grok processor. - Extract structured fields out of a single text field within a document. +

                    Run a grok processor.

                    +

                    Extract structured fields out of a single text field within a document. You must choose which field to extract matched fields from, as well as the grok pattern you expect will match. A grok pattern is like a regular expression that supports aliased expressions that can be reused.

                    @@ -613,8 +613,8 @@ async def put_pipeline( """ .. raw:: html -

                    Create or update a pipeline. - Changes made using this API take effect immediately.

                    +

                    Create or update a pipeline.

                    +

                    Changes made using this API take effect immediately.

                    ``_ diff --git a/elasticsearch/_async/client/license.py b/elasticsearch/_async/client/license.py index 2a5c35189..952ec017e 100644 --- a/elasticsearch/_async/client/license.py +++ b/elasticsearch/_async/client/license.py @@ -312,8 +312,7 @@ async def post_start_basic( ``_ - :param acknowledge: whether the user has acknowledged acknowledge messages (default: - false) + :param acknowledge: Whether the user has acknowledged acknowledge messages :param master_timeout: Period to wait for a connection to the master node. :param timeout: Period to wait for a response. If no response is received before the timeout expires, the request fails and returns an error. @@ -360,8 +359,8 @@ async def post_start_trial( """ .. raw:: html -

                    Start a trial. - Start a 30-day trial, which gives access to all subscription features.

                    +

                    Start a trial.

                    +

                    Start a 30-day trial, which gives access to all subscription features.

                    NOTE: You are allowed to start a trial only if your cluster has not already activated a trial for the current major product version. For example, if you have already activated a trial for v8.0, you cannot start a new trial until v9.0. You can, however, request an extended trial at https://www.elastic.co/trialextension.

                    To check the status of your trial, use the get trial status API.

                    @@ -369,10 +368,9 @@ async def post_start_trial( ``_ - :param acknowledge: whether the user has acknowledged acknowledge messages (default: - false) + :param acknowledge: Whether the user has acknowledged acknowledge messages :param master_timeout: Period to wait for a connection to the master node. - :param type: The type of trial license to generate (default: "trial") + :param type: The type of trial license to generate """ __path_parts: t.Dict[str, str] = {} __path = "/_license/start_trial" diff --git a/elasticsearch/_async/client/logstash.py b/elasticsearch/_async/client/logstash.py index c724911dc..bc6639925 100644 --- a/elasticsearch/_async/client/logstash.py +++ b/elasticsearch/_async/client/logstash.py @@ -38,8 +38,8 @@ async def delete_pipeline( """ .. raw:: html -

                    Delete a Logstash pipeline. - Delete a pipeline that is used for Logstash Central Management. +

                    Delete a Logstash pipeline.

                    +

                    Delete a pipeline that is used for Logstash Central Management. If the request succeeds, you receive an empty response with an appropriate status code.

                    @@ -83,8 +83,8 @@ async def get_pipeline( """ .. raw:: html -

                    Get Logstash pipelines. - Get pipelines that are used for Logstash Central Management.

                    +

                    Get Logstash pipelines.

                    +

                    Get pipelines that are used for Logstash Central Management.

                    ``_ diff --git a/elasticsearch/_async/client/migration.py b/elasticsearch/_async/client/migration.py index 4ff5a18fc..7fd2bee1f 100644 --- a/elasticsearch/_async/client/migration.py +++ b/elasticsearch/_async/client/migration.py @@ -38,8 +38,8 @@ async def deprecations( """ .. raw:: html -

                    Get deprecation information. - Get information about different cluster, node, and index level settings that use deprecated features that will be removed or changed in the next major version.

                    +

                    Get deprecation information.

                    +

                    Get information about different cluster, node, and index level settings that use deprecated features that will be removed or changed in the next major version.

                    TIP: This APIs is designed for indirect use by the Upgrade Assistant. You are strongly recommended to use the Upgrade Assistant.

                    @@ -87,8 +87,8 @@ async def get_feature_upgrade_status( """ .. raw:: html -

                    Get feature migration information. - Version upgrades sometimes require changes to how features store configuration information and data in system indices. +

                    Get feature migration information.

                    +

                    Version upgrades sometimes require changes to how features store configuration information and data in system indices. Check which features need to be migrated and the status of any migrations that are in progress.

                    TIP: This API is designed for indirect use by the Upgrade Assistant. You are strongly recommended to use the Upgrade Assistant.

                    @@ -129,8 +129,8 @@ async def post_feature_upgrade( """ .. raw:: html -

                    Start the feature migration. - Version upgrades sometimes require changes to how features store configuration information and data in system indices. +

                    Start the feature migration.

                    +

                    Version upgrades sometimes require changes to how features store configuration information and data in system indices. This API starts the automatic migration process.

                    Some functionality might be temporarily unavailable during the migration process.

                    TIP: The API is designed for indirect use by the Upgrade Assistant. We strongly recommend you use the Upgrade Assistant.

                    diff --git a/elasticsearch/_async/client/ml.py b/elasticsearch/_async/client/ml.py index f6cd5d834..c658e1318 100644 --- a/elasticsearch/_async/client/ml.py +++ b/elasticsearch/_async/client/ml.py @@ -1108,8 +1108,8 @@ async def flush_job( """ .. raw:: html -

                    Force buffered data to be processed. - The flush jobs API is only applicable when sending data for analysis using +

                    Force buffered data to be processed.

                    +

                    The flush jobs API is only applicable when sending data for analysis using the post data API. Depending on the content of the buffer, then it might additionally calculate new results. Both flush and close operations are similar, however the flush is more efficient if you are expecting to send @@ -1276,8 +1276,8 @@ async def get_buckets( """ .. raw:: html -

                    Get anomaly detection job results for buckets. - The API presents a chronological view of the records, grouped by bucket.

                    +

                    Get anomaly detection job results for buckets.

                    +

                    The API presents a chronological view of the records, grouped by bucket.

                    ``_ @@ -1605,8 +1605,8 @@ async def get_data_frame_analytics( """ .. raw:: html -

                    Get data frame analytics job configuration info. - You can get information for multiple data frame analytics jobs in a single +

                    Get data frame analytics job configuration info.

                    +

                    You can get information for multiple data frame analytics jobs in a single API request by using a comma-separated list of data frame analytics jobs or a wildcard expression.

                    @@ -1751,8 +1751,8 @@ async def get_datafeed_stats( """ .. raw:: html -

                    Get datafeed stats. - You can get statistics for multiple datafeeds in a single API request by +

                    Get datafeed stats.

                    +

                    You can get statistics for multiple datafeeds in a single API request by using a comma-separated list of datafeeds or a wildcard expression. You can get statistics for all datafeeds by using _all, by specifying * as the <feed_id>, or by omitting the <feed_id>. If the datafeed is stopped, the @@ -1816,8 +1816,8 @@ async def get_datafeeds( """ .. raw:: html -

                    Get datafeeds configuration info. - You can get information for multiple datafeeds in a single API request by +

                    Get datafeeds configuration info.

                    +

                    You can get information for multiple datafeeds in a single API request by using a comma-separated list of datafeeds or a wildcard expression. You can get information for all datafeeds by using _all, by specifying * as the <feed_id>, or by omitting the <feed_id>. @@ -1887,8 +1887,8 @@ async def get_filters( """ .. raw:: html -

                    Get filters. - You can get a single filter or all filters.

                    +

                    Get filters.

                    +

                    You can get a single filter or all filters.

                    ``_ @@ -1953,8 +1953,8 @@ async def get_influencers( """ .. raw:: html -

                    Get anomaly detection job results for influencers. - Influencers are the entities that have contributed to, or are to blame for, +

                    Get anomaly detection job results for influencers.

                    +

                    Influencers are the entities that have contributed to, or are to blame for, the anomalies. Influencer results are available only if an influencer_field_name is specified in the job configuration.

                    @@ -2100,8 +2100,8 @@ async def get_jobs( """ .. raw:: html -

                    Get anomaly detection jobs configuration info. - You can get information for multiple anomaly detection jobs in a single API +

                    Get anomaly detection jobs configuration info.

                    +

                    You can get information for multiple anomaly detection jobs in a single API request by using a group name, a comma-separated list of jobs, or a wildcard expression. You can get information for all anomaly detection jobs by using _all, by specifying * as the <job_id>, or by omitting the <job_id>.

                    @@ -2168,8 +2168,8 @@ async def get_memory_stats( """ .. raw:: html -

                    Get machine learning memory usage info. - Get information about how machine learning jobs and trained models are using memory, +

                    Get machine learning memory usage info.

                    +

                    Get information about how machine learning jobs and trained models are using memory, on each node, both within the JVM heap, and natively, outside of the JVM.

                    @@ -2522,8 +2522,8 @@ async def get_records( """ .. raw:: html -

                    Get anomaly records for an anomaly detection job. - Records contain the detailed analytical results. They describe the anomalous +

                    Get anomaly records for an anomaly detection job.

                    +

                    Records contain the detailed analytical results. They describe the anomalous activity that has been identified in the input data based on the detector configuration. There can be many anomaly records depending on the characteristics and size @@ -2715,8 +2715,8 @@ async def get_trained_models_stats( """ .. raw:: html -

                    Get trained models usage info. - You can get usage information for multiple trained +

                    Get trained models usage info.

                    +

                    You can get usage information for multiple trained models in a single API request by using a comma-separated list of model IDs or a wildcard expression.

                    @@ -2843,8 +2843,8 @@ async def info( """ .. raw:: html -

                    Get machine learning information. - Get defaults and limits used by machine learning. +

                    Get machine learning information.

                    +

                    Get defaults and limits used by machine learning. This endpoint is designed to be used by a user interface that needs to fully understand machine learning configurations where some options are not specified, meaning that the defaults should be used. This endpoint may be @@ -3052,10 +3052,7 @@ async def post_data( if reset_start is not None: __query["reset_start"] = reset_start __body = data if data is not None else body - __headers = { - "accept": "application/json", - "content-type": "application/x-ndjson", - } + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", __path, @@ -3083,8 +3080,8 @@ async def preview_data_frame_analytics( """ .. raw:: html -

                    Preview features used by data frame analytics. - Preview the extracted features used by a data frame analytics config.

                    +

                    Preview features used by data frame analytics.

                    +

                    Preview the extracted features used by a data frame analytics config.

                    ``_ @@ -3149,8 +3146,8 @@ async def preview_datafeed( """ .. raw:: html -

                    Preview a datafeed. - This API returns the first "page" of search results from a datafeed. +

                    Preview a datafeed.

                    +

                    This API returns the first "page" of search results from a datafeed. You can preview an existing datafeed or provide configuration details for a datafeed and anomaly detection job in the API. The preview shows the structure of the data that will be passed to the anomaly detection engine. @@ -3371,8 +3368,8 @@ async def put_data_frame_analytics( """ .. raw:: html -

                    Create a data frame analytics job. - This API creates a data frame analytics job that performs an analysis on the +

                    Create a data frame analytics job.

                    +

                    This API creates a data frame analytics job that performs an analysis on the source indices and stores the outcome in a destination index. By default, the query used in the source configuration is {"match_all": {}}.

                    If the destination index does not exist, it is created automatically when you start the job.

                    @@ -3552,8 +3549,8 @@ async def put_datafeed( """ .. raw:: html -

                    Create a datafeed. - Datafeeds retrieve data from Elasticsearch for analysis by an anomaly detection job. +

                    Create a datafeed.

                    +

                    Datafeeds retrieve data from Elasticsearch for analysis by an anomaly detection job. You can associate only one datafeed with each anomaly detection job. The datafeed contains a query that runs at a defined interval (frequency). If you are concerned about delayed data, you can add a delay (query_delay') at each interval. By default, the datafeed uses the following query: {"match_all": {"boost": 1}}`.

                    @@ -3721,8 +3718,8 @@ async def put_filter( """ .. raw:: html -

                    Create a filter. - A filter contains a list of strings. It can be used by one or more anomaly detection jobs. +

                    Create a filter.

                    +

                    A filter contains a list of strings. It can be used by one or more anomaly detection jobs. Specifically, filters are referenced in the custom_rules property of detector configuration objects.

                    @@ -4026,8 +4023,8 @@ async def put_trained_model( """ .. raw:: html -

                    Create a trained model. - Enable you to supply a trained model that is not created by data frame analytics.

                    +

                    Create a trained model.

                    +

                    Enable you to supply a trained model that is not created by data frame analytics.

                    ``_ @@ -4132,8 +4129,8 @@ async def put_trained_model_alias( """ .. raw:: html -

                    Create or update a trained model alias. - A trained model alias is a logical name used to reference a single trained +

                    Create or update a trained model alias.

                    +

                    A trained model alias is a logical name used to reference a single trained model. You can use aliases instead of trained model identifiers to make it easier to reference your models. For example, you can use aliases in inference @@ -4289,8 +4286,8 @@ async def put_trained_model_vocabulary( """ .. raw:: html -

                    Create a trained model vocabulary. - This API is supported only for natural language processing (NLP) models. +

                    Create a trained model vocabulary.

                    +

                    This API is supported only for natural language processing (NLP) models. The vocabulary is stored in the index as described in inference_config.*.vocabulary of the trained model definition.

                    @@ -4350,8 +4347,8 @@ async def reset_job( """ .. raw:: html -

                    Reset an anomaly detection job. - All model state and results are deleted. The job is ready to start over as if +

                    Reset an anomaly detection job.

                    +

                    All model state and results are deleted. The job is ready to start over as if it had just been created. It is not currently possible to reset multiple jobs using wildcards or a comma separated list.

                    @@ -4411,8 +4408,8 @@ async def revert_model_snapshot( """ .. raw:: html -

                    Revert to a snapshot. - The machine learning features react quickly to anomalous input, learning new +

                    Revert to a snapshot.

                    +

                    The machine learning features react quickly to anomalous input, learning new behaviors in data. Highly anomalous input increases the variance in the models whilst the system learns whether this is a new step-change in behavior or a one-off event. In the case where this anomalous input is known to be a @@ -4481,8 +4478,8 @@ async def set_upgrade_mode( """ .. raw:: html -

                    Set upgrade_mode for ML indices. - Sets a cluster wide upgrade_mode setting that prepares machine learning +

                    Set upgrade_mode for ML indices.

                    +

                    Sets a cluster wide upgrade_mode setting that prepares machine learning indices for an upgrade. When upgrading your cluster, in some circumstances you must restart your nodes and reindex your machine learning indices. In those circumstances, @@ -4528,7 +4525,9 @@ async def set_upgrade_mode( path_parts=__path_parts, ) - @_rewrite_parameters() + @_rewrite_parameters( + body_fields=("timeout",), + ) async def start_data_frame_analytics( self, *, @@ -4538,12 +4537,13 @@ async def start_data_frame_analytics( human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + body: t.Optional[t.Dict[str, t.Any]] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html -

                    Start a data frame analytics job. - A data frame analytics job can be started and stopped multiple times +

                    Start a data frame analytics job.

                    +

                    A data frame analytics job can be started and stopped multiple times throughout its lifecycle. If the destination index does not exist, it is created automatically the first time you start the data frame analytics job. The @@ -4569,6 +4569,7 @@ async def start_data_frame_analytics( __path_parts: t.Dict[str, str] = {"id": _quote(id)} __path = f'/_ml/data_frame/analytics/{__path_parts["id"]}/_start' __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = body if body is not None else {} if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -4577,14 +4578,20 @@ async def start_data_frame_analytics( __query["human"] = human if pretty is not None: __query["pretty"] = pretty - if timeout is not None: - __query["timeout"] = timeout + if not __body: + if timeout is not None: + __body["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" return await self.perform_request( # type: ignore[return-value] "POST", __path, params=__query, headers=__headers, + body=__body, endpoint_id="ml.start_data_frame_analytics", path_parts=__path_parts, ) @@ -4692,8 +4699,8 @@ async def start_trained_model_deployment( """ .. raw:: html -

                    Start a trained model deployment. - It allocates the model to every machine learning node.

                    +

                    Start a trained model deployment.

                    +

                    It allocates the model to every machine learning node.

                    ``_ @@ -4714,7 +4721,7 @@ async def start_trained_model_deployment( is greater than the number of hardware threads it will automatically be changed to a value less than the number of hardware threads. If adaptive_allocations is enabled, do not set this value, because it’s automatically set. - :param priority: The deployment priority. + :param priority: The deployment priority :param queue_capacity: Specifies the number of inference requests that are allowed in the queue. After the number of requests exceeds this value, new requests are rejected with a 429 error. @@ -4776,7 +4783,9 @@ async def start_trained_model_deployment( path_parts=__path_parts, ) - @_rewrite_parameters() + @_rewrite_parameters( + body_fields=("allow_no_match", "force", "timeout"), + ) async def stop_data_frame_analytics( self, *, @@ -4788,12 +4797,13 @@ async def stop_data_frame_analytics( human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + body: t.Optional[t.Dict[str, t.Any]] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html -

                    Stop data frame analytics jobs. - A data frame analytics job can be started and stopped multiple times +

                    Stop data frame analytics jobs.

                    +

                    A data frame analytics job can be started and stopped multiple times throughout its lifecycle.

                    @@ -4819,26 +4829,33 @@ async def stop_data_frame_analytics( __path_parts: t.Dict[str, str] = {"id": _quote(id)} __path = f'/_ml/data_frame/analytics/{__path_parts["id"]}/_stop' __query: t.Dict[str, t.Any] = {} - if allow_no_match is not None: - __query["allow_no_match"] = allow_no_match + __body: t.Dict[str, t.Any] = body if body is not None else {} if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: __query["filter_path"] = filter_path - if force is not None: - __query["force"] = force if human is not None: __query["human"] = human if pretty is not None: __query["pretty"] = pretty - if timeout is not None: - __query["timeout"] = timeout + if not __body: + if allow_no_match is not None: + __body["allow_no_match"] = allow_no_match + if force is not None: + __body["force"] = force + if timeout is not None: + __body["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" return await self.perform_request( # type: ignore[return-value] "POST", __path, params=__query, headers=__headers, + body=__body, endpoint_id="ml.stop_data_frame_analytics", path_parts=__path_parts, ) @@ -4862,8 +4879,8 @@ async def stop_datafeed( """ .. raw:: html -

                    Stop datafeeds. - A datafeed that is stopped ceases to retrieve data from Elasticsearch. A datafeed can be started and stopped +

                    Stop datafeeds.

                    +

                    A datafeed that is stopped ceases to retrieve data from Elasticsearch. A datafeed can be started and stopped multiple times throughout its lifecycle.

                    @@ -4914,7 +4931,9 @@ async def stop_datafeed( path_parts=__path_parts, ) - @_rewrite_parameters() + @_rewrite_parameters( + body_fields=("allow_no_match", "force", "id"), + ) async def stop_trained_model_deployment( self, *, @@ -4924,7 +4943,9 @@ async def stop_trained_model_deployment( filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, force: t.Optional[bool] = None, human: t.Optional[bool] = None, + id: t.Optional[str] = None, pretty: t.Optional[bool] = None, + body: t.Optional[t.Dict[str, t.Any]] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html @@ -4944,30 +4965,40 @@ async def stop_trained_model_deployment( no matches or only partial matches. :param force: Forcefully stops the deployment, even if it is used by ingest pipelines. You can't use these pipelines until you restart the model deployment. + :param id: If provided, must be the same identifier as in the path. """ if model_id in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'model_id'") __path_parts: t.Dict[str, str] = {"model_id": _quote(model_id)} __path = f'/_ml/trained_models/{__path_parts["model_id"]}/deployment/_stop' __query: t.Dict[str, t.Any] = {} - if allow_no_match is not None: - __query["allow_no_match"] = allow_no_match + __body: t.Dict[str, t.Any] = body if body is not None else {} if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: __query["filter_path"] = filter_path - if force is not None: - __query["force"] = force if human is not None: __query["human"] = human if pretty is not None: __query["pretty"] = pretty + if not __body: + if allow_no_match is not None: + __body["allow_no_match"] = allow_no_match + if force is not None: + __body["force"] = force + if id is not None: + __body["id"] = id + if not __body: + __body = None # type: ignore[assignment] __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" return await self.perform_request( # type: ignore[return-value] "POST", __path, params=__query, headers=__headers, + body=__body, endpoint_id="ml.stop_trained_model_deployment", path_parts=__path_parts, ) @@ -5108,8 +5139,8 @@ async def update_datafeed( """ .. raw:: html -

                    Update a datafeed. - You must stop and start the datafeed for the changes to be applied. +

                    Update a datafeed.

                    +

                    You must stop and start the datafeed for the changes to be applied. When Elasticsearch security features are enabled, your datafeed remembers which roles the user who updated it had at the time of the update and runs the query using those same roles. If you provide secondary authorization headers, those credentials are used instead.

                    @@ -5272,8 +5303,8 @@ async def update_filter( """ .. raw:: html -

                    Update a filter. - Updates the description of a filter, adds items, or removes items from the list.

                    +

                    Update a filter.

                    +

                    Updates the description of a filter, adds items, or removes items from the list.

                    ``_ @@ -5366,8 +5397,8 @@ async def update_job( """ .. raw:: html -

                    Update an anomaly detection job. - Updates certain properties of an anomaly detection job.

                    +

                    Update an anomaly detection job.

                    +

                    Updates certain properties of an anomaly detection job.

                    ``_ @@ -5498,8 +5529,8 @@ async def update_model_snapshot( """ .. raw:: html -

                    Update a snapshot. - Updates certain properties of a snapshot.

                    +

                    Update a snapshot.

                    +

                    Updates certain properties of a snapshot.

                    ``_ @@ -5632,8 +5663,8 @@ async def upgrade_job_snapshot( """ .. raw:: html -

                    Upgrade a snapshot. - Upgrade an anomaly detection model snapshot to the latest major version. +

                    Upgrade a snapshot.

                    +

                    Upgrade an anomaly detection model snapshot to the latest major version. Over time, older snapshot formats are deprecated and removed. Anomaly detection jobs support only snapshots that are from the current or previous major version. diff --git a/elasticsearch/_async/client/monitoring.py b/elasticsearch/_async/client/monitoring.py index 541bb0319..54e6dd343 100644 --- a/elasticsearch/_async/client/monitoring.py +++ b/elasticsearch/_async/client/monitoring.py @@ -45,8 +45,8 @@ async def bulk( """ .. raw:: html -

                    Send monitoring data. - This API is used by the monitoring features to send monitoring data.

                    +

                    Send monitoring data.

                    +

                    This API is used by the monitoring features to send monitoring data.

                    ``_ diff --git a/elasticsearch/_async/client/nodes.py b/elasticsearch/_async/client/nodes.py index f6b92d663..c9af87b2f 100644 --- a/elasticsearch/_async/client/nodes.py +++ b/elasticsearch/_async/client/nodes.py @@ -46,8 +46,8 @@ async def clear_repositories_metering_archive( """ .. raw:: html -

                    Clear the archived repositories metering. - Clear the archived repositories metering information in the cluster.

                    +

                    Clear the archived repositories metering.

                    +

                    Clear the archived repositories metering information in the cluster.

                    ``_ @@ -99,8 +99,8 @@ async def get_repositories_metering_info( """ .. raw:: html -

                    Get cluster repositories metering. - Get repositories metering information for a cluster. +

                    Get cluster repositories metering.

                    +

                    Get repositories metering information for a cluster. This API exposes monotonically non-decreasing counters and it is expected that clients would durably store the information needed to compute aggregations over a period of time. Additionally, the information exposed by this API is volatile, meaning that it will not be present after node restarts.

                    @@ -157,8 +157,8 @@ async def hot_threads( """ .. raw:: html -

                    Get the hot threads for nodes. - Get a breakdown of the hot threads on each selected node in the cluster. +

                    Get the hot threads for nodes.

                    +

                    Get a breakdown of the hot threads on each selected node in the cluster. The output is plain text with a breakdown of the top hot threads for each node.

                    @@ -169,7 +169,7 @@ async def hot_threads( select, or to get a task from an empty queue) are filtered out. :param interval: The interval to do the second sampling of threads. :param snapshots: Number of samples of thread stacktrace. - :param sort: The sort order for 'cpu' type (default: total) + :param sort: The sort order for 'cpu' type :param threads: Specifies the number of hot threads to provide information for. :param timeout: Period to wait for a response. If no response is received before the timeout expires, the request fails and returns an error. @@ -376,8 +376,8 @@ async def stats( """ .. raw:: html -

                    Get node statistics. - Get statistics for nodes in a cluster. +

                    Get node statistics.

                    +

                    Get statistics for nodes in a cluster. By default, all stats are returned. You can limit the returned information by using metrics.

                    @@ -385,7 +385,7 @@ async def stats( :param node_id: Comma-separated list of node IDs or names used to limit returned information. - :param metric: Limit the information returned to the specified metrics + :param metric: Limits the information returned to the specific metrics. :param index_metric: Limit the information returned for indices metric to the specific index metrics. It can be used only if indices (or all) metric is specified. @@ -499,8 +499,8 @@ async def usage( ``_ :param node_id: A comma-separated list of node IDs or names to limit the returned - information; use `_local` to return information from the node you're connecting - to, leave empty to get information from all nodes + information. Use `_local` to return information from the node you're connecting + to, leave empty to get information from all nodes. :param metric: Limits the information returned to the specific metrics. A comma-separated list of the following options: `_all`, `rest_actions`. :param timeout: Period to wait for a response. If no response is received before diff --git a/elasticsearch/_async/client/project.py b/elasticsearch/_async/client/project.py index dee5a2c20..fd1036679 100644 --- a/elasticsearch/_async/client/project.py +++ b/elasticsearch/_async/client/project.py @@ -42,7 +42,8 @@ async def tags( """ .. raw:: html -

                    Return tags defined for the project

                    +

                    Get tags.

                    +

                    Get the tags that are defined for the project.

                    """ __path_parts: t.Dict[str, str] = {} diff --git a/elasticsearch/_async/client/query_rules.py b/elasticsearch/_async/client/query_rules.py index 4e056f817..ec9d478f4 100644 --- a/elasticsearch/_async/client/query_rules.py +++ b/elasticsearch/_async/client/query_rules.py @@ -39,8 +39,8 @@ async def delete_rule( """ .. raw:: html -

                    Delete a query rule. - Delete a query rule within a query ruleset. +

                    Delete a query rule.

                    +

                    Delete a query rule within a query ruleset. This is a destructive action that is only recoverable by re-adding the same rule with the create or update query rule API.

                    @@ -92,8 +92,8 @@ async def delete_ruleset( """ .. raw:: html -

                    Delete a query ruleset. - Remove a query ruleset and its associated data. +

                    Delete a query ruleset.

                    +

                    Remove a query ruleset and its associated data. This is a destructive action that is not recoverable.

                    @@ -138,8 +138,8 @@ async def get_rule( """ .. raw:: html -

                    Get a query rule. - Get details about a query rule within a query ruleset.

                    +

                    Get a query rule.

                    +

                    Get details about a query rule within a query ruleset.

                    ``_ @@ -190,8 +190,8 @@ async def get_ruleset( """ .. raw:: html -

                    Get a query ruleset. - Get details about a query ruleset.

                    +

                    Get a query ruleset.

                    +

                    Get details about a query ruleset.

                    ``_ @@ -237,8 +237,8 @@ async def list_rulesets( """ .. raw:: html -

                    Get all query rulesets. - Get summarized information about the query rulesets.

                    +

                    Get all query rulesets.

                    +

                    Get summarized information about the query rulesets.

                    ``_ @@ -294,8 +294,8 @@ async def put_rule( """ .. raw:: html -

                    Create or update a query rule. - Create or update a query rule within a query ruleset.

                    +

                    Create or update a query rule.

                    +

                    Create or update a query rule within a query ruleset.

                    IMPORTANT: Due to limitations within pinned queries, you can only pin documents using ids or docs, but cannot use both in single rule. It is advised to use one or the other in query rulesets, to avoid errors. Additionally, pinned queries have a maximum limit of 100 pinned hits. @@ -380,8 +380,8 @@ async def put_ruleset( """ .. raw:: html -

                    Create or update a query ruleset. - There is a limit of 100 rules per ruleset. +

                    Create or update a query ruleset.

                    +

                    There is a limit of 100 rules per ruleset. This limit can be increased by using the xpack.applications.rules.max_rules_per_ruleset cluster setting.

                    IMPORTANT: Due to limitations within pinned queries, you can only select documents using ids or docs, but cannot use both in single rule. It is advised to use one or the other in query rulesets, to avoid errors. @@ -442,8 +442,8 @@ async def test( """ .. raw:: html -

                    Test a query ruleset. - Evaluate match criteria against a query ruleset to identify the rules that would match that criteria.

                    +

                    Test a query ruleset.

                    +

                    Evaluate match criteria against a query ruleset to identify the rules that would match that criteria.

                    ``_ diff --git a/elasticsearch/_async/client/rollup.py b/elasticsearch/_async/client/rollup.py index 83503e9b5..75d9f70b2 100644 --- a/elasticsearch/_async/client/rollup.py +++ b/elasticsearch/_async/client/rollup.py @@ -108,8 +108,8 @@ async def get_jobs( """ .. raw:: html -

                    Get rollup job information. - Get the configuration, stats, and status of rollup jobs.

                    +

                    Get rollup job information.

                    +

                    Get the configuration, stats, and status of rollup jobs.

                    NOTE: This API returns only active (both STARTED and STOPPED) jobs. If a job was created, ran for a while, then was deleted, the API does not return any details about it. For details about a historical rollup job, the rollup capabilities API may be more useful.

                    @@ -160,8 +160,8 @@ async def get_rollup_caps( """ .. raw:: html -

                    Get the rollup job capabilities. - Get the capabilities of any rollup jobs that have been configured for a specific index or index pattern.

                    +

                    Get the rollup job capabilities.

                    +

                    Get the capabilities of any rollup jobs that have been configured for a specific index or index pattern.

                    This API is useful because a rollup job is often configured to rollup only a subset of fields from the source index. Furthermore, only certain aggregations can be configured for various fields, leading to a limited subset of functionality depending on that configuration. This API enables you to inspect an index and determine:

                    @@ -216,8 +216,8 @@ async def get_rollup_index_caps( """ .. raw:: html -

                    Get the rollup index capabilities. - Get the rollup capabilities of all jobs inside of a rollup index. +

                    Get the rollup index capabilities.

                    +

                    Get the rollup capabilities of all jobs inside of a rollup index. A single rollup index may store the data for multiple rollup jobs and may have a variety of capabilities depending on those jobs. This API enables you to determine:

                    • What jobs are stored in an index (or indices specified via a pattern)?
                    • @@ -412,8 +412,8 @@ async def rollup_search( """ .. raw:: html -

                      Search rolled-up data. - The rollup search endpoint is needed because, internally, rolled-up documents utilize a different document structure than the original data. +

                      Search rolled-up data.

                      +

                      The rollup search endpoint is needed because, internally, rolled-up documents utilize a different document structure than the original data. It rewrites standard Query DSL into a format that matches the rollup documents then takes the response and rewrites it back to what a client would expect given the original query.

                      The request body supports a subset of features from the regular search API. The following functionality is not available:

                      @@ -495,8 +495,8 @@ async def start_job( """ .. raw:: html -

                      Start rollup jobs. - If you try to start a job that does not exist, an exception occurs. +

                      Start rollup jobs.

                      +

                      If you try to start a job that does not exist, an exception occurs. If you try to start a job that is already started, nothing happens.

                      @@ -543,8 +543,8 @@ async def stop_job( """ .. raw:: html -

                      Stop rollup jobs. - If you try to stop a job that does not exist, an exception occurs. +

                      Stop rollup jobs.

                      +

                      If you try to stop a job that does not exist, an exception occurs. If you try to stop a job that is already stopped, nothing happens.

                      Since only a stopped job can be deleted, it can be useful to block the API until the indexer has fully stopped. This is accomplished with the wait_for_completion query parameter, and optionally a timeout. For example:

                      diff --git a/elasticsearch/_async/client/search_application.py b/elasticsearch/_async/client/search_application.py index c3a5bf629..30e57740c 100644 --- a/elasticsearch/_async/client/search_application.py +++ b/elasticsearch/_async/client/search_application.py @@ -90,8 +90,8 @@ async def delete_behavioral_analytics( """ .. raw:: html -

                      Delete a behavioral analytics collection. - The associated data stream is also deleted.

                      +

                      Delete a behavioral analytics collection.

                      +

                      The associated data stream is also deleted.

                      ``_ @@ -230,8 +230,8 @@ async def list( """ .. raw:: html -

                      Get search applications. - Get information about search applications.

                      +

                      Get search applications.

                      +

                      Get information about search applications.

                      ``_ @@ -460,8 +460,8 @@ async def render_query( """ .. raw:: html -

                      Render a search application query. - Generate an Elasticsearch query using the specified query parameters and the search template associated with the search application or a default template if none is specified. +

                      Render a search application query.

                      +

                      Generate an Elasticsearch query using the specified query parameters and the search template associated with the search application or a default template if none is specified. If a parameter used in the search template is not specified in params, the parameter's default value will be used. The API returns the specific Elasticsearch query that would be generated and run by calling the search application search API.

                      You must have read privileges on the backing alias of the search application.

                      @@ -526,8 +526,8 @@ async def search( """ .. raw:: html -

                      Run a search application search. - Generate and run an Elasticsearch query that uses the specified query parameteter and the search template associated with the search application or default template. +

                      Run a search application search.

                      +

                      Generate and run an Elasticsearch query that uses the specified query parameteter and the search template associated with the search application or default template. Unspecified template parameters are assigned their default values if applicable.

                      diff --git a/elasticsearch/_async/client/searchable_snapshots.py b/elasticsearch/_async/client/searchable_snapshots.py index 822fd8785..539932dd3 100644 --- a/elasticsearch/_async/client/searchable_snapshots.py +++ b/elasticsearch/_async/client/searchable_snapshots.py @@ -46,8 +46,8 @@ async def cache_stats( """ .. raw:: html -

                      Get cache statistics. - Get statistics about the shared cache for partially mounted indices.

                      +

                      Get cache statistics.

                      +

                      Get statistics about the shared cache for partially mounted indices.

                      ``_ @@ -107,8 +107,8 @@ async def clear_cache( """ .. raw:: html -

                      Clear the cache. - Clear indices and data streams from the shared cache for partially mounted indices.

                      +

                      Clear the cache.

                      +

                      Clear indices and data streams from the shared cache for partially mounted indices.

                      ``_ @@ -119,7 +119,7 @@ async def clear_cache( into no concrete indices. (This includes `_all` string or when no indices have been specified) :param expand_wildcards: Whether to expand wildcard expression to concrete indices - that are open, closed or both. + that are open, closed or both :param ignore_unavailable: Whether specified concrete indices should be ignored when unavailable (missing or closed) """ @@ -184,8 +184,8 @@ async def mount( """ .. raw:: html -

                      Mount a snapshot. - Mount a snapshot as a searchable snapshot index. +

                      Mount a snapshot.

                      +

                      Mount a snapshot as a searchable snapshot index. Do not use this API for snapshots managed by index lifecycle management (ILM). Manually mounting ILM-managed snapshots can interfere with ILM processes.

                      diff --git a/elasticsearch/_async/client/security.py b/elasticsearch/_async/client/security.py index de094aa03..bc5878a2b 100644 --- a/elasticsearch/_async/client/security.py +++ b/elasticsearch/_async/client/security.py @@ -288,8 +288,8 @@ async def bulk_update_api_keys( """ .. raw:: html -

                      Bulk update API keys. - Update the attributes for multiple API keys.

                      +

                      Bulk update API keys.

                      +

                      Update the attributes for multiple API keys.

                      IMPORTANT: It is not possible to use an API key as the authentication credential for this API. To update API keys, the owner user's credentials are required.

                      This API is similar to the update API key API but enables you to apply the same update to multiple API keys in one API call. This operation can greatly improve performance over making individual updates.

                      It is not possible to update expired or invalidated API keys.

                      @@ -892,8 +892,8 @@ async def create_service_token( Token names must be unique in the context of the associated service account. They must also be globally unique with their fully qualified names, which are comprised of the service account principal and token name, such as `//`. - :param refresh: If `true` then refresh the affected shards to make this operation - visible to search, if `wait_for` (the default) then wait for a refresh to + :param refresh: If `true` (the default) then refresh the affected shards to make + this operation visible to search, if `wait_for` then wait for a refresh to make this operation visible to search, if `false` then do nothing with refreshes. """ if namespace in SKIP_IN_PATH: @@ -1208,8 +1208,8 @@ async def delete_service_token( :param namespace: The namespace, which is a top-level grouping of service accounts. :param service: The service name. :param name: The name of the service account token. - :param refresh: If `true` then refresh the affected shards to make this operation - visible to search, if `wait_for` (the default) then wait for a refresh to + :param refresh: If `true` (the default) then refresh the affected shards to make + this operation visible to search, if `wait_for` then wait for a refresh to make this operation visible to search, if `false` then do nothing with refreshes. """ if namespace in SKIP_IN_PATH: @@ -3750,7 +3750,8 @@ async def query_role( :param size: The number of hits to return. It must not be negative. By default, you cannot page through more than 10,000 hits using the `from` and `size` parameters. To page through more hits, use the `search_after` parameter. - :param sort: The sort definition. You can sort on `username`, `roles`, or `enabled`. + :param sort: The sort definition. You can sort on `name`, `description`, `metadata`, + `applications.application`, `applications.privileges`, and `applications.resources`. In addition, sort can also be applied to the `_doc` field to sort by index order. """ diff --git a/elasticsearch/_async/client/shutdown.py b/elasticsearch/_async/client/shutdown.py index 231d1a208..70b809ffc 100644 --- a/elasticsearch/_async/client/shutdown.py +++ b/elasticsearch/_async/client/shutdown.py @@ -48,8 +48,8 @@ async def delete_node( """ .. raw:: html -

                      Cancel node shutdown preparations. - Remove a node from the shutdown list so it can resume normal operations. +

                      Cancel node shutdown preparations.

                      +

                      Remove a node from the shutdown list so it can resume normal operations. You must explicitly clear the shutdown request when a node rejoins the cluster or when a node has permanently left the cluster. Shutdown requests are never removed automatically by Elasticsearch.

                      NOTE: This feature is designed for indirect use by Elastic Cloud, Elastic Cloud Enterprise, and Elastic Cloud on Kubernetes. @@ -117,7 +117,8 @@ async def get_node( ``_ - :param node_id: Which node for which to retrieve the shutdown status + :param node_id: Comma-separated list of nodes for which to retrieve the shutdown + status :param master_timeout: Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. diff --git a/elasticsearch/_async/client/simulate.py b/elasticsearch/_async/client/simulate.py index 25d7ea826..f284fb6d5 100644 --- a/elasticsearch/_async/client/simulate.py +++ b/elasticsearch/_async/client/simulate.py @@ -67,8 +67,8 @@ async def ingest( """ .. raw:: html -

                      Simulate data ingestion. - Run ingest pipelines against a set of provided documents, optionally with substitute pipeline definitions, to simulate ingesting data into an index.

                      +

                      Simulate data ingestion.

                      +

                      Run ingest pipelines against a set of provided documents, optionally with substitute pipeline definitions, to simulate ingesting data into an index.

                      This API is meant to be used for troubleshooting or pipeline development, as it does not actually index any data into Elasticsearch.

                      The API runs the default and final pipeline for that index against a set of documents provided in the body of the request. If a pipeline contains a reroute processor, it follows that reroute processor to the new index, running that index's pipelines as well the same way that a non-simulated ingest would. diff --git a/elasticsearch/_async/client/slm.py b/elasticsearch/_async/client/slm.py index 88f0d910b..a157eb73e 100644 --- a/elasticsearch/_async/client/slm.py +++ b/elasticsearch/_async/client/slm.py @@ -40,8 +40,8 @@ async def delete_lifecycle( """ .. raw:: html -

                      Delete a policy. - Delete a snapshot lifecycle policy definition. +

                      Delete a policy.

                      +

                      Delete a snapshot lifecycle policy definition. This operation prevents any future snapshots from being taken but does not cancel in-progress snapshots or remove previously-taken snapshots.

                      @@ -96,8 +96,8 @@ async def execute_lifecycle( """ .. raw:: html -

                      Run a policy. - Immediately create a snapshot according to the snapshot lifecycle policy without waiting for the scheduled time. +

                      Run a policy.

                      +

                      Immediately create a snapshot according to the snapshot lifecycle policy without waiting for the scheduled time. The snapshot policy is normally applied according to its schedule, but you might want to manually run a policy before performing an upgrade or other maintenance.

                      @@ -151,8 +151,8 @@ async def execute_retention( """ .. raw:: html -

                      Run a retention policy. - Manually apply the retention policy to force immediate removal of snapshots that are expired according to the snapshot lifecycle policy retention rules. +

                      Run a retention policy.

                      +

                      Manually apply the retention policy to force immediate removal of snapshots that are expired according to the snapshot lifecycle policy retention rules. The retention policy is normally applied according to its schedule.

                      @@ -204,13 +204,13 @@ async def get_lifecycle( """ .. raw:: html -

                      Get policy information. - Get snapshot lifecycle policy definitions and information about the latest snapshot attempts.

                      +

                      Get policy information.

                      +

                      Get snapshot lifecycle policy definitions and information about the latest snapshot attempts.

                      ``_ - :param policy_id: Comma-separated list of snapshot lifecycle policies to retrieve + :param policy_id: A comma-separated list of snapshot lifecycle policy identifiers. :param master_timeout: The period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. @@ -261,8 +261,8 @@ async def get_stats( """ .. raw:: html -

                      Get snapshot lifecycle management statistics. - Get global and policy-level statistics about actions taken by snapshot lifecycle management.

                      +

                      Get snapshot lifecycle management statistics.

                      +

                      Get global and policy-level statistics about actions taken by snapshot lifecycle management.

                      ``_ @@ -373,8 +373,8 @@ async def put_lifecycle( """ .. raw:: html -

                      Create or update a policy. - Create or update a snapshot lifecycle policy. +

                      Create or update a policy.

                      +

                      Create or update a snapshot lifecycle policy. If the policy already exists, this request increments the policy version. Only the latest version of a policy is stored.

                      @@ -456,8 +456,8 @@ async def start( """ .. raw:: html -

                      Start snapshot lifecycle management. - Snapshot lifecycle management (SLM) starts automatically when a cluster is formed. +

                      Start snapshot lifecycle management.

                      +

                      Snapshot lifecycle management (SLM) starts automatically when a cluster is formed. Manually starting SLM is necessary only if it has been stopped using the stop SLM API.

                      @@ -510,8 +510,8 @@ async def stop( """ .. raw:: html -

                      Stop snapshot lifecycle management. - Stop all snapshot lifecycle management (SLM) operations and the SLM plugin. +

                      Stop snapshot lifecycle management.

                      +

                      Stop all snapshot lifecycle management (SLM) operations and the SLM plugin. This API is useful when you are performing maintenance on a cluster and need to prevent SLM from performing any actions on your data streams or indices. Stopping SLM does not stop any snapshots that are in progress. You can manually trigger snapshots with the run snapshot lifecycle policy API even if SLM is stopped.

                      diff --git a/elasticsearch/_async/client/snapshot.py b/elasticsearch/_async/client/snapshot.py index 2c8d7f00d..84a83416b 100644 --- a/elasticsearch/_async/client/snapshot.py +++ b/elasticsearch/_async/client/snapshot.py @@ -46,8 +46,8 @@ async def cleanup_repository( """ .. raw:: html -

                      Clean up the snapshot repository. - Trigger the review of the contents of a snapshot repository and delete any stale data not referenced by existing snapshots.

                      +

                      Clean up the snapshot repository.

                      +

                      Trigger the review of the contents of a snapshot repository and delete any stale data not referenced by existing snapshots.

                      ``_ @@ -110,8 +110,8 @@ async def clone( """ .. raw:: html -

                      Clone a snapshot. - Clone part of all of a snapshot into another snapshot in the same repository.

                      +

                      Clone a snapshot.

                      +

                      Clone part of all of a snapshot into another snapshot in the same repository.

                      ``_ @@ -207,8 +207,8 @@ async def create( """ .. raw:: html -

                      Create a snapshot. - Take a snapshot of a cluster or of data streams and indices.

                      +

                      Create a snapshot.

                      +

                      Take a snapshot of a cluster or of data streams and indices.

                      ``_ @@ -330,8 +330,8 @@ async def create_repository( """ .. raw:: html -

                      Create or update a snapshot repository. - IMPORTANT: If you are migrating searchable snapshots, the repository name must be identical in the source and destination clusters. +

                      Create or update a snapshot repository.

                      +

                      IMPORTANT: If you are migrating searchable snapshots, the repository name must be identical in the source and destination clusters. To register a snapshot repository, the cluster's global metadata must be writeable. Ensure there are no cluster blocks (for example, cluster.blocks.read_only and clsuter.blocks.read_only_allow_delete settings) that prevent write access.

                      Several options for this API can be specified using a query parameter or a request body parameter. @@ -470,8 +470,8 @@ async def delete_repository( """ .. raw:: html -

                      Delete snapshot repositories. - When a repository is unregistered, Elasticsearch removes only the reference to the location where the repository is storing the snapshots. +

                      Delete snapshot repositories.

                      +

                      When a repository is unregistered, Elasticsearch removes only the reference to the location where the repository is storing the snapshots. The snapshots themselves are left untouched and in place.

                      @@ -976,8 +976,8 @@ async def repository_verify_integrity( """ .. raw:: html -

                      Verify the repository integrity. - Verify the integrity of the contents of a snapshot repository.

                      +

                      Verify the repository integrity.

                      +

                      Verify the integrity of the contents of a snapshot repository.

                      This API enables you to perform a comprehensive check of the contents of a repository, looking for any anomalies in its data or metadata which might prevent you from restoring snapshots from the repository or which might cause future snapshot create or delete operations to fail.

                      If you suspect the integrity of the contents of one of your snapshot repositories, cease all write activity to this repository immediately, set its read_only option to true, and use this API to verify its integrity. Until you do so:

                      @@ -1115,8 +1115,8 @@ async def restore( """ .. raw:: html -

                      Restore a snapshot. - Restore a snapshot of a cluster or data streams and indices.

                      +

                      Restore a snapshot.

                      +

                      Restore a snapshot of a cluster or data streams and indices.

                      You can restore a snapshot only to a running cluster with an elected master node. The snapshot repository must be registered and available to the cluster. The snapshot and cluster versions must be compatible.

                      @@ -1264,8 +1264,8 @@ async def status( """ .. raw:: html -

                      Get the snapshot status. - Get a detailed description of the current state for each shard participating in the snapshot.

                      +

                      Get the snapshot status.

                      +

                      Get a detailed description of the current state for each shard participating in the snapshot.

                      Note that this API should be used only to obtain detailed shard-level information for ongoing snapshots. If this detail is not needed or you want to obtain information about one or more existing snapshots, use the get snapshot API.

                      If you omit the <snapshot> request path parameter, the request retrieves information only for currently running snapshots. @@ -1347,8 +1347,8 @@ async def verify_repository( """ .. raw:: html -

                      Verify a snapshot repository. - Check for common misconfigurations in a snapshot repository.

                      +

                      Verify a snapshot repository.

                      +

                      Check for common misconfigurations in a snapshot repository.

                      ``_ diff --git a/elasticsearch/_async/client/sql.py b/elasticsearch/_async/client/sql.py index 9e146e0f8..73ea1b0f5 100644 --- a/elasticsearch/_async/client/sql.py +++ b/elasticsearch/_async/client/sql.py @@ -89,8 +89,8 @@ async def delete_async( """ .. raw:: html -

                      Delete an async SQL search. - Delete an async SQL search or a stored synchronous SQL search. +

                      Delete an async SQL search.

                      +

                      Delete an async SQL search or a stored synchronous SQL search. If the search is still running, the API cancels it.

                      If the Elasticsearch security features are enabled, only the following users can use this API to delete a search:

                        @@ -145,8 +145,8 @@ async def get_async( """ .. raw:: html -

                        Get async SQL search results. - Get the current status and available results for an async SQL search or stored synchronous SQL search.

                        +

                        Get async SQL search results.

                        +

                        Get the current status and available results for an async SQL search or stored synchronous SQL search.

                        If the Elasticsearch security features are enabled, only the user who first submitted the SQL search can retrieve the search using this API.

                        @@ -208,8 +208,8 @@ async def get_async_status( """ .. raw:: html -

                        Get the async SQL search status. - Get the current status of an async SQL search or a stored synchronous SQL search.

                        +

                        Get the async SQL search status.

                        +

                        Get the current status of an async SQL search or a stored synchronous SQL search.

                        ``_ @@ -298,8 +298,8 @@ async def query( """ .. raw:: html -

                        Get SQL search results. - Run an SQL request.

                        +

                        Get SQL search results.

                        +

                        Run an SQL request.

                        ``_ @@ -429,8 +429,8 @@ async def translate( """ .. raw:: html -

                        Translate SQL into Elasticsearch queries. - Translate an SQL search into a search API request containing Query DSL. +

                        Translate SQL into Elasticsearch queries.

                        +

                        Translate an SQL search into a search API request containing Query DSL. It accepts the same request body parameters as the SQL search API, excluding cursor.

                        diff --git a/elasticsearch/_async/client/streams.py b/elasticsearch/_async/client/streams.py index 1310bfa2b..de76e1bc4 100644 --- a/elasticsearch/_async/client/streams.py +++ b/elasticsearch/_async/client/streams.py @@ -72,7 +72,7 @@ async def logs_disable( __query["pretty"] = pretty if timeout is not None: __query["timeout"] = timeout - __headers = {"accept": "application/json,text/plain"} + __headers = {"accept": "text/plain,application/json"} return await self.perform_request( # type: ignore[return-value] "POST", __path, @@ -127,7 +127,7 @@ async def logs_enable( __query["pretty"] = pretty if timeout is not None: __query["timeout"] = timeout - __headers = {"accept": "application/json,text/plain"} + __headers = {"accept": "text/plain,application/json"} return await self.perform_request( # type: ignore[return-value] "POST", __path, diff --git a/elasticsearch/_async/client/synonyms.py b/elasticsearch/_async/client/synonyms.py index 2466dfb6c..34edb0c77 100644 --- a/elasticsearch/_async/client/synonyms.py +++ b/elasticsearch/_async/client/synonyms.py @@ -95,8 +95,8 @@ async def delete_synonym_rule( """ .. raw:: html -

                        Delete a synonym rule. - Delete a synonym rule from a synonym set.

                        +

                        Delete a synonym rule.

                        +

                        Delete a synonym rule from a synonym set.

                        ``_ @@ -204,8 +204,8 @@ async def get_synonym_rule( """ .. raw:: html -

                        Get a synonym rule. - Get a synonym rule from a synonym set.

                        +

                        Get a synonym rule.

                        +

                        Get a synonym rule from a synonym set.

                        ``_ @@ -257,8 +257,8 @@ async def get_synonyms_sets( """ .. raw:: html -

                        Get all synonym sets. - Get a summary of all defined synonym sets.

                        +

                        Get all synonym sets.

                        +

                        Get a summary of all defined synonym sets.

                        ``_ @@ -311,8 +311,8 @@ async def put_synonym( """ .. raw:: html -

                        Create or update a synonym set. - Synonyms sets are limited to a maximum of 10,000 synonym rules per set. +

                        Create or update a synonym set.

                        +

                        Synonyms sets are limited to a maximum of 10,000 synonym rules per set. If you need to manage more synonym rules, you can create multiple synonym sets.

                        When an existing synonyms set is updated, the search analyzers that use the synonyms set are reloaded automatically for all indices. This is equivalent to invoking the reload search analyzers API for all indices that use the synonyms set.

                        @@ -378,8 +378,8 @@ async def put_synonym_rule( """ .. raw:: html -

                        Create or update a synonym rule. - Create or update a synonym rule in a synonym set.

                        +

                        Create or update a synonym rule.

                        +

                        Create or update a synonym rule in a synonym set.

                        If any of the synonym rules included is invalid, the API returns an error.

                        When you update a synonym rule, all analyzers using the synonyms set will be reloaded automatically to reflect the new rule.

                        diff --git a/elasticsearch/_async/client/tasks.py b/elasticsearch/_async/client/tasks.py index 93376baa1..53e2faaaa 100644 --- a/elasticsearch/_async/client/tasks.py +++ b/elasticsearch/_async/client/tasks.py @@ -121,8 +121,8 @@ async def get( """ .. raw:: html -

                        Get task information. - Get information about a task currently running in the cluster.

                        +

                        Get task information.

                        +

                        Get information about a task currently running in the cluster.

                        WARNING: The task management API is new and should still be considered a beta feature. The API may change in ways that are not backwards compatible.

                        If the task identifier is not found, a 404 response code indicates that there are no resources that match the request.

                        @@ -185,8 +185,8 @@ async def list( """ .. raw:: html -

                        Get all tasks. - Get information about the tasks currently running on one or more nodes in the cluster.

                        +

                        Get all tasks.

                        +

                        Get information about the tasks currently running on one or more nodes in the cluster.

                        WARNING: The task management API is new and should still be considered a beta feature. The API may change in ways that are not backwards compatible.

                        Identifying running tasks

                        diff --git a/elasticsearch/_async/client/text_structure.py b/elasticsearch/_async/client/text_structure.py index 6307f20bb..98d3cbf82 100644 --- a/elasticsearch/_async/client/text_structure.py +++ b/elasticsearch/_async/client/text_structure.py @@ -55,8 +55,8 @@ async def find_field_structure( """ .. raw:: html -

                        Find the structure of a text field. - Find the structure of a text field in an Elasticsearch index.

                        +

                        Find the structure of a text field.

                        +

                        Find the structure of a text field in an Elasticsearch index.

                        This API provides a starting point for extracting further information from log messages already ingested into Elasticsearch. For example, if you have ingested data into a very simple index that has just @timestamp and message fields, you can use this API to see what common structure exists in the message field.

                        The response from the API contains:

                        @@ -241,8 +241,8 @@ async def find_message_structure( """ .. raw:: html -

                        Find the structure of text messages. - Find the structure of a list of text messages. +

                        Find the structure of text messages.

                        +

                        Find the structure of a list of text messages. The messages must contain data that is suitable to be ingested into Elasticsearch.

                        This API provides a starting point for ingesting data into Elasticsearch in a format that is suitable for subsequent use with other Elastic Stack functionality. Use this API rather than the find text structure API if your input text has already been split up into separate messages by some other process.

                        @@ -402,7 +402,11 @@ async def find_structure( delimiter: t.Optional[str] = None, ecs_compatibility: t.Optional[str] = None, explain: t.Optional[bool] = None, - format: t.Optional[str] = None, + format: t.Optional[ + t.Union[ + str, t.Literal["delimited", "ndjson", "semi_structured_text", "xml"] + ] + ] = None, grok_pattern: t.Optional[str] = None, has_header_row: t.Optional[bool] = None, line_merge_size_limit: t.Optional[int] = None, @@ -416,8 +420,8 @@ async def find_structure( """ .. raw:: html -

                        Find the structure of a text file. - The text file must contain data that is suitable to be ingested into Elasticsearch.

                        +

                        Find the structure of a text file.

                        +

                        The text file must contain data that is suitable to be ingested into Elasticsearch.

                        This API provides a starting point for ingesting data into Elasticsearch in a format that is suitable for subsequent use with other Elastic Stack functionality. Unlike other Elasticsearch endpoints, the data that is posted to this endpoint does not need to be UTF-8 encoded and in JSON format. It must, however, be text; binary text formats are not currently supported. @@ -615,8 +619,8 @@ async def test_grok_pattern( """ .. raw:: html -

                        Test a Grok pattern. - Test a Grok pattern on one or more lines of text. +

                        Test a Grok pattern.

                        +

                        Test a Grok pattern on one or more lines of text. The API indicates whether the lines match the pattern together with the offsets and lengths of the matched substrings.

                        diff --git a/elasticsearch/_async/client/transform.py b/elasticsearch/_async/client/transform.py index 249fa35cb..165ddf6d1 100644 --- a/elasticsearch/_async/client/transform.py +++ b/elasticsearch/_async/client/transform.py @@ -85,6 +85,45 @@ async def delete_transform( path_parts=__path_parts, ) + @_rewrite_parameters() + async def get_node_stats( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + .. raw:: html + +

                        Get node stats.

                        +

                        Get per-node information about transform usage.

                        + + + ``_ + """ + __path_parts: t.Dict[str, str] = {} + __path = "/_transform/_node_stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return await self.perform_request( # type: ignore[return-value] + "GET", + __path, + params=__query, + headers=__headers, + endpoint_id="transform.get_node_stats", + path_parts=__path_parts, + ) + @_rewrite_parameters( parameter_aliases={"from": "from_"}, ) @@ -104,8 +143,8 @@ async def get_transform( """ .. raw:: html -

                        Get transforms. - Get configuration information for transforms.

                        +

                        Get transforms.

                        +

                        Get configuration information for transforms.

                        ``_ @@ -262,8 +301,8 @@ async def preview_transform( """ .. raw:: html -

                        Preview a transform. - Generates a preview of the results that you will get when you create a transform with the same configuration.

                        +

                        Preview a transform.

                        +

                        Generates a preview of the results that you will get when you create a transform with the same configuration.

                        It returns a maximum of 100 results. The calculations are based on all the current data in the source index. It also generates a list of mappings and settings for the destination index. These values are determined based on the field types of the source index and the transform aggregations.

                        @@ -386,8 +425,8 @@ async def put_transform( """ .. raw:: html -

                        Create a transform. - Creates a transform.

                        +

                        Create a transform.

                        +

                        Creates a transform.

                        A transform copies data from source indices, transforms it, and persists it into an entity-centric destination index. You can also think of the destination index as a two-dimensional tabular data structure (known as a data frame). The ID for each document in the data frame is generated from a hash of the entity, so there is a unique row per entity.

                        @@ -616,8 +655,8 @@ async def set_upgrade_mode( """ .. raw:: html -

                        Set upgrade_mode for transform indices. - Sets a cluster wide upgrade_mode setting that prepares transform +

                        Set upgrade_mode for transform indices.

                        +

                        Sets a cluster wide upgrade_mode setting that prepares transform indices for an upgrade. When upgrading your cluster, in some circumstances you must restart your nodes and reindex your transform indices. In those circumstances, @@ -749,8 +788,8 @@ async def stop_transform( """ .. raw:: html -

                        Stop transforms. - Stops one or more transforms.

                        +

                        Stop transforms.

                        +

                        Stops one or more transforms.

                        ``_ @@ -846,8 +885,8 @@ async def update_transform( """ .. raw:: html -

                        Update a transform. - Updates certain properties of a transform.

                        +

                        Update a transform.

                        +

                        Updates certain properties of a transform.

                        All updated properties except description do not take effect until after the transform starts the next checkpoint, thus there is data consistency in each checkpoint. To use this API, you must have read and view_index_metadata privileges for the source indices. You must also have index and read privileges for the destination index. When diff --git a/elasticsearch/_async/client/watcher.py b/elasticsearch/_async/client/watcher.py index 8558b9920..147d235bd 100644 --- a/elasticsearch/_async/client/watcher.py +++ b/elasticsearch/_async/client/watcher.py @@ -39,8 +39,8 @@ async def ack_watch( """ .. raw:: html -

                        Acknowledge a watch. - Acknowledging a watch enables you to manually throttle the execution of the watch's actions.

                        +

                        Acknowledge a watch.

                        +

                        Acknowledging a watch enables you to manually throttle the execution of the watch's actions.

                        The acknowledgement state of an action is stored in the status.actions.<id>.ack.state structure.

                        IMPORTANT: If the specified watch is currently being executed, this API will return an error The reason for this behavior is to prevent overwriting the watch status from a watch execution.

                        @@ -101,8 +101,8 @@ async def activate_watch( """ .. raw:: html -

                        Activate a watch. - A watch can be either active or inactive.

                        +

                        Activate a watch.

                        +

                        A watch can be either active or inactive.

                        ``_ @@ -145,8 +145,8 @@ async def deactivate_watch( """ .. raw:: html -

                        Deactivate a watch. - A watch can be either active or inactive.

                        +

                        Deactivate a watch.

                        +

                        A watch can be either active or inactive.

                        ``_ @@ -189,8 +189,8 @@ async def delete_watch( """ .. raw:: html -

                        Delete a watch. - When the watch is removed, the document representing the watch in the .watches index is gone and it will never be run again.

                        +

                        Delete a watch.

                        +

                        When the watch is removed, the document representing the watch in the .watches index is gone and it will never be run again.

                        Deleting a watch does not delete any watch execution records related to this watch from the watch history.

                        IMPORTANT: Deleting a watch must be done by using only this API. Do not delete the watch directly from the .watches index using the Elasticsearch delete document API @@ -266,8 +266,8 @@ async def execute_watch( """ .. raw:: html -

                        Run a watch. - This API can be used to force execution of the watch outside of its triggering logic or to simulate the watch execution for debugging purposes.

                        +

                        Run a watch.

                        +

                        This API can be used to force execution of the watch outside of its triggering logic or to simulate the watch execution for debugging purposes.

                        For testing and debugging purposes, you also have fine-grained control on how the watch runs. You can run the watch without running all of its actions or alternatively by simulating them. You can also force execution by ignoring the watch condition and control whether a watch record would be written to the watch history after it runs.

                        @@ -362,8 +362,8 @@ async def get_settings( """ .. raw:: html -

                        Get Watcher index settings. - Get settings for the Watcher internal index (.watches). +

                        Get Watcher index settings.

                        +

                        Get settings for the Watcher internal index (.watches). Only a subset of settings are shown, for example index.auto_expand_replicas and index.number_of_replicas.

                        @@ -476,8 +476,8 @@ async def put_watch( """ .. raw:: html -

                        Create or update a watch. - When a watch is registered, a new document that represents the watch is added to the .watches index and its trigger is immediately registered with the relevant trigger engine. +

                        Create or update a watch.

                        +

                        When a watch is registered, a new document that represents the watch is added to the .watches index and its trigger is immediately registered with the relevant trigger engine. Typically for the schedule trigger, the scheduler is the trigger engine.

                        IMPORTANT: You must use Kibana or this API to create a watch. Do not add a watch directly to the .watches index by using the Elasticsearch index API. @@ -494,9 +494,9 @@ async def put_watch( :param active: The initial state of the watch. The default value is `true`, which means the watch is active by default. :param condition: The condition that defines if the actions should be run. - :param if_primary_term: only update the watch if the last operation that has + :param if_primary_term: Only update the watch if the last operation that has changed the watch has the specified primary term - :param if_seq_no: only update the watch if the last operation that has changed + :param if_seq_no: Only update the watch if the last operation that has changed the watch has the specified sequence number :param input: The input that defines the input that loads the data for the watch. :param metadata: Metadata JSON that will be copied into the history entries. @@ -591,8 +591,8 @@ async def query_watches( """ .. raw:: html -

                        Query watches. - Get all registered watches in a paginated manner and optionally filter watches by a query.

                        +

                        Query watches.

                        +

                        Get all registered watches in a paginated manner and optionally filter watches by a query.

                        Note that only the _id and metadata.* fields are queryable or sortable.

                        @@ -667,8 +667,8 @@ async def start( """ .. raw:: html -

                        Start the watch service. - Start the Watcher service if it is not already running.

                        +

                        Start the watch service.

                        +

                        Start the Watcher service if it is not already running.

                        ``_ @@ -732,8 +732,8 @@ async def stats( """ .. raw:: html -

                        Get Watcher statistics. - This API always returns basic metrics. +

                        Get Watcher statistics.

                        +

                        This API always returns basic metrics. You retrieve more metrics by using the metric parameter.

                        @@ -784,8 +784,8 @@ async def stop( """ .. raw:: html -

                        Stop the watch service. - Stop the Watcher service if it is running.

                        +

                        Stop the watch service.

                        +

                        Stop the Watcher service if it is running.

                        ``_ @@ -840,8 +840,8 @@ async def update_settings( """ .. raw:: html -

                        Update Watcher index settings. - Update settings for the Watcher internal index (.watches). +

                        Update Watcher index settings.

                        +

                        Update settings for the Watcher internal index (.watches). Only a subset of settings can be modified. This includes index.auto_expand_replicas, index.number_of_replicas, index.routing.allocation.exclude.*, index.routing.allocation.include.* and index.routing.allocation.require.*. diff --git a/elasticsearch/_async/client/xpack.py b/elasticsearch/_async/client/xpack.py index 710d36cb1..9365e1c21 100644 --- a/elasticsearch/_async/client/xpack.py +++ b/elasticsearch/_async/client/xpack.py @@ -45,8 +45,8 @@ async def info( """ .. raw:: html -

                        Get information. - The information provided by the API includes:

                        +

                        Get information.

                        +

                        The information provided by the API includes:

                        • Build information including the build number and timestamp.
                        • License information about the currently installed license.
                        • @@ -56,7 +56,8 @@ async def info( ``_ - :param accept_enterprise: If this param is used it must be set to true + :param accept_enterprise: If used, this otherwise ignored parameter must be set + to true :param categories: A comma-separated list of the information categories to include in the response. For example, `build,license,features`. """ @@ -98,8 +99,8 @@ async def usage( """ .. raw:: html -

                          Get usage information. - Get information about the features that are currently enabled and available under the current license. +

                          Get usage information.

                          +

                          Get information about the features that are currently enabled and available under the current license. The API also provides some usage statistics.

                          diff --git a/elasticsearch/_sync/client/__init__.py b/elasticsearch/_sync/client/__init__.py index 875221797..48506bce1 100644 --- a/elasticsearch/_sync/client/__init__.py +++ b/elasticsearch/_sync/client/__init__.py @@ -567,8 +567,8 @@ def bulk( """ .. raw:: html -

                          Bulk index or delete documents. - Perform multiple index, create, delete, and update actions in a single request. +

                          Bulk index or delete documents.

                          +

                          Perform multiple index, create, delete, and update actions in a single request. This reduces overhead and can greatly increase indexing speed.

                          If the Elasticsearch security features are enabled, you must have the following index privileges for the target data stream, index, or index alias:

                            @@ -616,6 +616,7 @@ def bulk(
                          • Perl: Check out Search::Elasticsearch::Client::5_0::Bulk and Search::Elasticsearch::Client::5_0::Scroll
                          • Python: Check out elasticsearch.helpers.*
                          • JavaScript: Check out client.helpers.*
                          • +
                          • Java: Check out co.elastic.clients.elasticsearch._helpers.bulk.BulkIngester
                          • .NET: Check out BulkAllObservable
                          • PHP: Check out bulk indexing.
                          • Ruby: Check out Elasticsearch::Helpers::BulkHelper
                          • @@ -773,8 +774,8 @@ def clear_scroll( """ .. raw:: html -

                            Clear a scrolling search. - Clear the search context and results for a scrolling search.

                            +

                            Clear a scrolling search.

                            +

                            Clear the search context and results for a scrolling search.

                            ``_ @@ -827,8 +828,8 @@ def close_point_in_time( """ .. raw:: html -

                            Close a point in time. - A point in time must be opened explicitly before being used in search requests. +

                            Close a point in time.

                            +

                            A point in time must be opened explicitly before being used in search requests. The keep_alive parameter tells Elasticsearch how long it should persist. A point in time is automatically closed when the keep_alive period has elapsed. However, keeping points in time has a cost; close them as soon as they are no longer required for search requests.

                            @@ -905,8 +906,8 @@ def count( """ .. raw:: html -

                            Count search results. - Get the number of documents matching a query.

                            +

                            Count search results.

                            +

                            Get the number of documents matching a query.

                            The query can be provided either by using a simple query string as a parameter, or by defining Query DSL within the request body. The query is optional. When no query is provided, the API uses match_all to count all the documents.

                            The count API supports multi-target syntax. You can run a single count API search across multiple data streams and indices.

                            @@ -1648,11 +1649,11 @@ def delete_by_query_rethrottle( self, *, task_id: str, + requests_per_second: float, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, - requests_per_second: t.Optional[float] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html @@ -1670,9 +1671,13 @@ def delete_by_query_rethrottle( """ if task_id in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'task_id'") + if requests_per_second is None: + raise ValueError("Empty value passed for parameter 'requests_per_second'") __path_parts: t.Dict[str, str] = {"task_id": _quote(task_id)} __path = f'/_delete_by_query/{__path_parts["task_id"]}/_rethrottle' __query: t.Dict[str, t.Any] = {} + if requests_per_second is not None: + __query["requests_per_second"] = requests_per_second if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -1681,8 +1686,6 @@ def delete_by_query_rethrottle( __query["human"] = human if pretty is not None: __query["pretty"] = pretty - if requests_per_second is not None: - __query["requests_per_second"] = requests_per_second __headers = {"accept": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", @@ -1708,8 +1711,8 @@ def delete_script( """ .. raw:: html -

                            Delete a script or search template. - Deletes a stored script or search template.

                            +

                            Delete a script or search template.

                            +

                            Deletes a stored script or search template.

                            ``_ @@ -2020,8 +2023,8 @@ def explain( """ .. raw:: html -

                            Explain a document match result. - Get information about why a specific document matches, or doesn't match, a query. +

                            Explain a document match result.

                            +

                            Get information about why a specific document matches, or doesn't match, a query. It computes a score explanation for a query and a specific document.

                            @@ -2437,8 +2440,8 @@ def get_script( """ .. raw:: html -

                            Get a script or search template. - Retrieves a stored script or search template.

                            +

                            Get a script or search template.

                            +

                            Retrieves a stored script or search template.

                            ``_ @@ -2674,8 +2677,8 @@ def health_report( """ .. raw:: html -

                            Get the cluster health. - Get a report with the health status of an Elasticsearch cluster. +

                            Get the cluster health.

                            +

                            Get a report with the health status of an Elasticsearch cluster. The report contains a list of indicators that compose Elasticsearch functionality.

                            Each indicator has a health status of: green, unknown, yellow or red. The indicator will provide an explanation and metadata describing the reason for its current health status.

                            @@ -2846,13 +2849,11 @@ def index( "id": "elkbee" } } - - In this example, the operation will succeed since the supplied version of 2 is higher than the current document version of 1. - If the document was already updated and its version was set to 2 or higher, the indexing command will fail and result in a conflict (409 HTTP status code). - - A nice side effect is that there is no need to maintain strict ordering of async indexing operations run as a result of changes to a source database, as long as version numbers from the source database are used. - Even the simple case of updating the Elasticsearch index using data from a database is simplified if external versioning is used, as only the latest version will be used if the index operations arrive out of order. +

                            In this example, the operation will succeed since the supplied version of 2 is higher than the current document version of 1. + If the document was already updated and its version was set to 2 or higher, the indexing command will fail and result in a conflict (409 HTTP status code).

                            +

                            A nice side effect is that there is no need to maintain strict ordering of async indexing operations run as a result of changes to a source database, as long as version numbers from the source database are used. + Even the simple case of updating the Elasticsearch index using data from a database is simplified if external versioning is used, as only the latest version will be used if the index operations arrive out of order.

                            ``_ @@ -2987,8 +2988,8 @@ def info( """ .. raw:: html -

                            Get cluster info. - Get basic build, version, and cluster information. +

                            Get cluster info.

                            +

                            Get basic build, version, and cluster information. ::: In Serverless, this API is retained for backward compatibility only. Some response fields, such as the version number, should be ignored.

                            @@ -3704,8 +3705,8 @@ def put_script( """ .. raw:: html -

                            Create or update a script or search template. - Creates or updates a stored script or search template.

                            +

                            Create or update a script or search template.

                            +

                            Creates or updates a stored script or search template.

                            ``_ @@ -4035,11 +4036,11 @@ def reindex_rethrottle( self, *, task_id: str, + requests_per_second: float, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, - requests_per_second: t.Optional[float] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html @@ -4063,9 +4064,13 @@ def reindex_rethrottle( """ if task_id in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'task_id'") + if requests_per_second is None: + raise ValueError("Empty value passed for parameter 'requests_per_second'") __path_parts: t.Dict[str, str] = {"task_id": _quote(task_id)} __path = f'/_reindex/{__path_parts["task_id"]}/_rethrottle' __query: t.Dict[str, t.Any] = {} + if requests_per_second is not None: + __query["requests_per_second"] = requests_per_second if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -4074,8 +4079,6 @@ def reindex_rethrottle( __query["human"] = human if pretty is not None: __query["pretty"] = pretty - if requests_per_second is not None: - __query["requests_per_second"] = requests_per_second __headers = {"accept": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", @@ -5193,11 +5196,19 @@ def search_mvt( ``_ - :param index: Comma-separated list of data streams, indices, or aliases to search - :param field: Field containing geospatial data to return - :param zoom: Zoom level for the vector tile to search - :param x: X coordinate for the vector tile to search - :param y: Y coordinate for the vector tile to search + :param index: A list of indices, data streams, or aliases to search. It supports + wildcards (`*`). To search all data streams and indices, omit this parameter + or use `*` or `_all`. To search a remote cluster, use the `:` + syntax. + :param field: A field that contains the geospatial data to return. It must be + a `geo_point` or `geo_shape` field. The field must have doc values enabled. + It cannot be a nested field. NOTE: Vector tiles do not natively support geometry + collections. For `geometrycollection` values in a `geo_shape` field, the + API returns a hits layer feature for each element of the collection. This + behavior may change in a future release. + :param zoom: The zoom level of the vector tile to search. It accepts `0` to `29`. + :param x: The X coordinate for the vector tile to search. + :param y: The Y coordinate for the vector tile to search. :param aggs: Sub-aggregations for the geotile_grid. It supports the following aggregation types: - `avg` - `boxplot` - `cardinality` - `extended stats` - `max` - `median absolute deviation` - `min` - `percentile` - `percentile-rank` @@ -6120,8 +6131,8 @@ def update_by_query( """ .. raw:: html -

                            Update documents. - Updates documents that match the specified query. +

                            Update documents.

                            +

                            Updates documents that match the specified query. If no query is specified, performs an update on every document in the data stream or index without modifying the source, which is useful for picking up mapping changes.

                            If the Elasticsearch security features are enabled, you must have the following index privileges for the target data stream, index, or alias:

                              @@ -6400,11 +6411,11 @@ def update_by_query_rethrottle( self, *, task_id: str, + requests_per_second: float, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, - requests_per_second: t.Optional[float] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html @@ -6422,9 +6433,13 @@ def update_by_query_rethrottle( """ if task_id in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'task_id'") + if requests_per_second is None: + raise ValueError("Empty value passed for parameter 'requests_per_second'") __path_parts: t.Dict[str, str] = {"task_id": _quote(task_id)} __path = f'/_update_by_query/{__path_parts["task_id"]}/_rethrottle' __query: t.Dict[str, t.Any] = {} + if requests_per_second is not None: + __query["requests_per_second"] = requests_per_second if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -6433,8 +6448,6 @@ def update_by_query_rethrottle( __query["human"] = human if pretty is not None: __query["pretty"] = pretty - if requests_per_second is not None: - __query["requests_per_second"] = requests_per_second __headers = {"accept": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", diff --git a/elasticsearch/_sync/client/async_search.py b/elasticsearch/_sync/client/async_search.py index 31e53f89b..43a6ffb37 100644 --- a/elasticsearch/_sync/client/async_search.py +++ b/elasticsearch/_sync/client/async_search.py @@ -358,7 +358,7 @@ def submit( :param allow_partial_search_results: Indicate if an error should be returned if there is a partial search failure or timeout :param analyze_wildcard: Specify whether wildcard and prefix queries should be - analyzed (default: false) + analyzed :param analyzer: The analyzer to use for the query string :param batched_reduce_size: Affects how often partial results become available, which happens whenever shard results are reduced. A partial reduction is @@ -374,7 +374,7 @@ def submit( values for field names matching these patterns in the hits.fields property of the response. :param expand_wildcards: Whether to expand wildcard expression to concrete indices - that are open, closed or both. + that are open, closed or both :param explain: If true, returns detailed information about score computation as part of a hit. :param ext: Configuration of search extensions defined by Elasticsearch plugins. @@ -407,7 +407,7 @@ def submit( you cannot specify an in the request path. :param post_filter: :param preference: Specify the node or shard the operation should be performed - on (default: random) + on :param profile: :param project_routing: Specifies a subset of projects to target for the search using project metadata tags in a subset of Lucene query syntax. Allowed Lucene diff --git a/elasticsearch/_sync/client/autoscaling.py b/elasticsearch/_sync/client/autoscaling.py index 56b9433cc..f1157f4ca 100644 --- a/elasticsearch/_sync/client/autoscaling.py +++ b/elasticsearch/_sync/client/autoscaling.py @@ -54,7 +54,7 @@ def delete_autoscaling_policy( ``_ - :param name: the name of the autoscaling policy + :param name: Name of the autoscaling policy :param master_timeout: Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. @@ -163,7 +163,7 @@ def get_autoscaling_policy( ``_ - :param name: the name of the autoscaling policy + :param name: Name of the autoscaling policy :param master_timeout: Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. @@ -219,7 +219,7 @@ def put_autoscaling_policy( ``_ - :param name: the name of the autoscaling policy + :param name: Name of the autoscaling policy :param policy: :param master_timeout: Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and diff --git a/elasticsearch/_sync/client/cat.py b/elasticsearch/_sync/client/cat.py index 6132b3c04..a3b0dd5e3 100644 --- a/elasticsearch/_sync/client/cat.py +++ b/elasticsearch/_sync/client/cat.py @@ -3308,10 +3308,20 @@ def segments( self, *, index: t.Optional[t.Union[str, t.Sequence[str]]] = None, + allow_closed: t.Optional[bool] = None, + allow_no_indices: t.Optional[bool] = None, bytes: t.Optional[ t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] ] = None, error_trace: t.Optional[bool] = None, + expand_wildcards: t.Optional[ + t.Union[ + t.Sequence[ + t.Union[str, t.Literal["all", "closed", "hidden", "none", "open"]] + ], + t.Union[str, t.Literal["all", "closed", "hidden", "none", "open"]], + ] + ] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, h: t.Optional[ @@ -3362,6 +3372,8 @@ def segments( ] = None, help: t.Optional[bool] = None, human: t.Optional[bool] = None, + ignore_throttled: t.Optional[bool] = None, + ignore_unavailable: t.Optional[bool] = None, local: t.Optional[bool] = None, master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, @@ -3385,6 +3397,14 @@ def segments( :param index: A comma-separated list of data streams, indices, and aliases used to limit the request. Supports wildcards (`*`). To target all data streams and indices, omit this parameter or use `*` or `_all`. + :param allow_closed: If true, allow closed indices to be returned in the response + otherwise if false, keep the legacy behaviour of throwing an exception if + index pattern matches closed indices + :param allow_no_indices: If false, the request returns an error if any wildcard + expression, index alias, or _all value targets only missing or closed indices. + This behavior applies even if the request targets other open indices. For + example, a request targeting foo*,bar* returns an error if an index starts + with foo but no index starts with bar. :param bytes: Sets the units for columns that contain a byte-size value. Note that byte-size value units work in terms of powers of 1024. For instance `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are @@ -3393,12 +3413,20 @@ def segments( least `1.0`. If given, byte-size values are rendered as an integer with no suffix, representing the value of the column in the chosen unit. Values that are not an exact multiple of the chosen unit are rounded down. + :param expand_wildcards: Type of index that wildcard expressions can match. If + the request can target data streams, this argument determines whether wildcard + expressions match hidden data streams. Supports comma-separated values, such + as open,hidden. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple wildcards. :param help: When set to `true` will output available columns. This option can't be combined with any other query string option. + :param ignore_throttled: If true, concrete, expanded or aliased indices are ignored + when frozen. + :param ignore_unavailable: If true, missing or closed indices are not included + in the response. :param local: If `true`, the request computes the list of selected nodes from the local cluster state. If `false` the list of selected nodes are computed from the cluster state of the master node. In both cases the coordinating @@ -3423,10 +3451,16 @@ def segments( __path_parts = {} __path = "/_cat/segments" __query: t.Dict[str, t.Any] = {} + if allow_closed is not None: + __query["allow_closed"] = allow_closed + if allow_no_indices is not None: + __query["allow_no_indices"] = allow_no_indices if bytes is not None: __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace + if expand_wildcards is not None: + __query["expand_wildcards"] = expand_wildcards if filter_path is not None: __query["filter_path"] = filter_path if format is not None: @@ -3437,6 +3471,10 @@ def segments( __query["help"] = help if human is not None: __query["human"] = human + if ignore_throttled is not None: + __query["ignore_throttled"] = ignore_throttled + if ignore_unavailable is not None: + __query["ignore_unavailable"] = ignore_unavailable if local is not None: __query["local"] = local if master_timeout is not None: diff --git a/elasticsearch/_sync/client/ccr.py b/elasticsearch/_sync/client/ccr.py index 0eec10516..37699bacc 100644 --- a/elasticsearch/_sync/client/ccr.py +++ b/elasticsearch/_sync/client/ccr.py @@ -125,8 +125,8 @@ def follow( """ .. raw:: html -

                              Create a follower. - Create a cross-cluster replication follower index that follows a specific leader index. +

                              Create a follower.

                              +

                              Create a cross-cluster replication follower index that follows a specific leader index. When the API returns, the follower index exists and cross-cluster replication starts replicating operations from the leader index to the follower index.

                              @@ -368,8 +368,8 @@ def forget_follower( """ .. raw:: html -

                              Forget a follower. - Remove the cross-cluster replication follower retention leases from the leader.

                              +

                              Forget a follower.

                              +

                              Remove the cross-cluster replication follower retention leases from the leader.

                              A following index takes out retention leases on its leader index. These leases are used to increase the likelihood that the shards of the leader index retain the history of operations that the shards of the following index need to run replication. When a follower index is converted to a regular index by the unfollow API (either by directly calling the API or by index lifecycle management tasks), these leases are removed. @@ -382,7 +382,7 @@ def forget_follower( ``_ - :param index: the name of the leader index for which specified follower retention + :param index: Name of the leader index for which specified follower retention leases should be removed :param follower_cluster: :param follower_index: @@ -640,8 +640,8 @@ def put_auto_follow_pattern( """ .. raw:: html -

                              Create or update auto-follow patterns. - Create a collection of cross-cluster replication auto-follow patterns for a remote cluster. +

                              Create or update auto-follow patterns.

                              +

                              Create a collection of cross-cluster replication auto-follow patterns for a remote cluster. Newly created indices on the remote cluster that match any of the patterns are automatically configured as follower indices. Indices on the remote cluster that were created before the auto-follow pattern was created will not be auto-followed even if they match the pattern.

                              This API can also be used to update auto-follow patterns. @@ -853,8 +853,8 @@ def resume_follow( """ .. raw:: html -

                              Resume a follower. - Resume a cross-cluster replication follower index that was paused. +

                              Resume a follower.

                              +

                              Resume a cross-cluster replication follower index that was paused. The follower index could have been paused with the pause follower API. Alternatively it could be paused due to replication that cannot be retried due to failures during following tasks. When this API returns, the follower index will resume fetching operations from the leader index.

                              @@ -862,7 +862,7 @@ def resume_follow( ``_ - :param index: The name of the follow index to resume following. + :param index: Name of the follow index to resume following :param master_timeout: Period to wait for a connection to the master node. :param max_outstanding_read_requests: :param max_outstanding_write_requests: diff --git a/elasticsearch/_sync/client/cluster.py b/elasticsearch/_sync/client/cluster.py index 46cb6059f..58424bd6d 100644 --- a/elasticsearch/_sync/client/cluster.py +++ b/elasticsearch/_sync/client/cluster.py @@ -47,8 +47,8 @@ def allocation_explain( """ .. raw:: html -

                              Explain the shard allocations. - Get explanations for shard allocations in the cluster. +

                              Explain the shard allocations.

                              +

                              Get explanations for shard allocations in the cluster. This API accepts the current_node, index, primary and shard parameters in the request body or in query parameters, but not in both at the same time. For unassigned shards, it provides an explanation for why the shard is unassigned. For assigned shards, it provides an explanation for why the shard is remaining on its current node and has not moved or rebalanced to another node. @@ -127,8 +127,8 @@ def delete_component_template( """ .. raw:: html -

                              Delete component templates. - Component templates are building blocks for constructing index templates that specify index mappings, settings, and aliases.

                              +

                              Delete component templates.

                              +

                              Component templates are building blocks for constructing index templates that specify index mappings, settings, and aliases.

                              ``_ @@ -182,8 +182,8 @@ def delete_voting_config_exclusions( """ .. raw:: html -

                              Clear cluster voting config exclusions. - Remove master-eligible nodes from the voting configuration exclusion list.

                              +

                              Clear cluster voting config exclusions.

                              +

                              Remove master-eligible nodes from the voting configuration exclusion list.

                              ``_ @@ -236,8 +236,8 @@ def exists_component_template( """ .. raw:: html -

                              Check component templates. - Returns information about whether a particular component template exists.

                              +

                              Check component templates.

                              +

                              Returns information about whether a particular component template exists.

                              ``_ @@ -296,8 +296,8 @@ def get_component_template( """ .. raw:: html -

                              Get component templates. - Get information about component templates.

                              +

                              Get component templates.

                              +

                              Get information about component templates.

                              ``_ @@ -306,7 +306,7 @@ def get_component_template( request. Wildcard (`*`) expressions are supported. :param flat_settings: If `true`, returns settings in flat format. :param include_defaults: Return all default configurations for the component - template (default: false) + template :param local: If `true`, the request retrieves information from the local node only. If `false`, information is retrieved from the master node. :param master_timeout: Period to wait for a connection to the master node. If @@ -572,8 +572,8 @@ def info( """ .. raw:: html -

                              Get cluster info. - Returns basic information about the cluster.

                              +

                              Get cluster info.

                              +

                              Returns basic information about the cluster.

                              ``_ @@ -618,8 +618,8 @@ def pending_tasks( """ .. raw:: html -

                              Get the pending cluster tasks. - Get information about cluster-level changes (such as create index, update mapping, allocate or fail shard) that have not yet taken effect.

                              +

                              Get the pending cluster tasks.

                              +

                              Get information about cluster-level changes (such as create index, update mapping, allocate or fail shard) that have not yet taken effect.

                              NOTE: This API returns a list of any pending updates to the cluster state. These are distinct from the tasks reported by the task management API which include periodic tasks and tasks initiated by the user, such as node stats, search queries, or create index requests. However, if a user-initiated task such as a create index command causes a cluster state update, the activity of this task might be reported by both task api and pending cluster tasks API.

                              @@ -674,8 +674,8 @@ def post_voting_config_exclusions( """ .. raw:: html -

                              Update voting configuration exclusions. - Update the cluster voting config exclusions by node IDs or node names. +

                              Update voting configuration exclusions.

                              +

                              Update the cluster voting config exclusions by node IDs or node names. By default, if there are more than three master-eligible nodes in the cluster and you remove fewer than half of the master-eligible nodes in the cluster at once, the voting configuration automatically shrinks. If you want to shrink the voting configuration to contain fewer than three nodes or to remove half or more of the master-eligible nodes in the cluster at once, use this API to remove departing nodes from the voting configuration manually. The API adds an entry for each specified node to the cluster’s voting configuration exclusions list. @@ -757,8 +757,8 @@ def put_component_template( """ .. raw:: html -

                              Create or update a component template. - Component templates are building blocks for constructing index templates that specify index mappings, settings, and aliases.

                              +

                              Create or update a component template.

                              +

                              Component templates are building blocks for constructing index templates that specify index mappings, settings, and aliases.

                              An index template can be composed of multiple component templates. To use a component template, specify it in an index template’s composed_of list. Component templates are only applied to new data streams and indices as part of a matching index template.

                              @@ -883,10 +883,10 @@ def put_settings( ``_ - :param flat_settings: Return settings in flat format (default: false) - :param master_timeout: Explicit operation timeout for connection to master node + :param flat_settings: Return settings in flat format + :param master_timeout: The period to wait for a connection to the master node. :param persistent: The settings that persist after the cluster restarts. - :param timeout: Explicit operation timeout + :param timeout: The period to wait for a response. :param transient: The settings that do not persist after the cluster restarts. """ __path_parts: t.Dict[str, str] = {} @@ -992,8 +992,8 @@ def reroute( """ .. raw:: html -

                              Reroute the cluster. - Manually change the allocation of individual shards in the cluster. +

                              Reroute the cluster.

                              +

                              Manually change the allocation of individual shards in the cluster. For example, a shard can be moved from one node to another explicitly, an allocation can be canceled, and an unassigned shard can be explicitly allocated to a specific node.

                              It is important to note that after processing any reroute commands Elasticsearch will perform rebalancing as normal (respecting the values of settings such as cluster.routing.rebalance.enable) in order to remain in a balanced state. For example, if the requested allocation includes moving a shard from node1 to node2 then this may cause a shard to be moved from node2 back to node1 to even things out.

                              @@ -1093,8 +1093,8 @@ def state( """ .. raw:: html -

                              Get the cluster state. - Get comprehensive information about the state of the cluster.

                              +

                              Get the cluster state.

                              +

                              Get comprehensive information about the state of the cluster.

                              The cluster state is an internal data structure which keeps track of a variety of information needed by every node, including the identity and attributes of the other nodes in the cluster; cluster-wide settings; index metadata, including the mapping and settings for each index; the location and status of every shard copy in the cluster.

                              The elected master node ensures that every node in the cluster has a copy of the same cluster state. This API lets you retrieve a representation of this internal state for debugging or diagnostic purposes. @@ -1111,19 +1111,19 @@ def state( ``_ - :param metric: Limit the information returned to the specified metrics + :param metric: Limit the information returned to the specified metrics. :param index: A comma-separated list of index names; use `_all` or empty string to perform the operation on all indices :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified) :param expand_wildcards: Whether to expand wildcard expression to concrete indices - that are open, closed or both. - :param flat_settings: Return settings in flat format (default: false) + that are open, closed or both + :param flat_settings: Return settings in flat format :param ignore_unavailable: Whether specified concrete indices should be ignored when unavailable (missing or closed) :param local: Return local information, do not retrieve the state from master - node (default: false) + node :param master_timeout: Timeout for waiting for new cluster state in case it is blocked :param wait_for_metadata_version: Wait for the metadata version to be equal or @@ -1194,8 +1194,8 @@ def stats( """ .. raw:: html -

                              Get cluster statistics. - Get basic index metrics (shard numbers, store size, memory usage) and information about the current nodes that form the cluster (number, roles, os, jvm versions, memory usage, cpu and installed plugins).

                              +

                              Get cluster statistics.

                              +

                              Get basic index metrics (shard numbers, store size, memory usage) and information about the current nodes that form the cluster (number, roles, os, jvm versions, memory usage, cpu and installed plugins).

                              ``_ diff --git a/elasticsearch/_sync/client/connector.py b/elasticsearch/_sync/client/connector.py index d3f3e5985..ac8e77ae1 100644 --- a/elasticsearch/_sync/client/connector.py +++ b/elasticsearch/_sync/client/connector.py @@ -644,8 +644,8 @@ def sync_job_check_in( """ .. raw:: html -

                              Check in a connector sync job. - Check in a connector sync job and set the last_seen field to the current time before updating it in the internal index.

                              +

                              Check in a connector sync job.

                              +

                              Check in a connector sync job and set the last_seen field to the current time before updating it in the internal index.

                              To sync data using self-managed connectors, you need to deploy the Elastic connector service on your own infrastructure. This service runs automatically on Elastic Cloud for Elastic managed connectors.

                              @@ -701,8 +701,8 @@ def sync_job_claim( """ .. raw:: html -

                              Claim a connector sync job. - This action updates the job status to in_progress and sets the last_seen and started_at timestamps to the current time. +

                              Claim a connector sync job.

                              +

                              This action updates the job status to in_progress and sets the last_seen and started_at timestamps to the current time. Additionally, it can set the sync_cursor property for the sync job.

                              This API is not intended for direct connector management by users. It supports the implementation of services that utilize the connector protocol to communicate with Elasticsearch.

                              @@ -820,8 +820,8 @@ def sync_job_error( """ .. raw:: html -

                              Set a connector sync job error. - Set the error field for a connector sync job and set its status to error.

                              +

                              Set a connector sync job error.

                              +

                              Set the error field for a connector sync job and set its status to error.

                              To sync data using self-managed connectors, you need to deploy the Elastic connector service on your own infrastructure. This service runs automatically on Elastic Cloud for Elastic managed connectors.

                              @@ -1087,8 +1087,8 @@ def sync_job_update_stats( """ .. raw:: html -

                              Set the connector sync job stats. - Stats include: deleted_document_count, indexed_document_count, indexed_document_volume, and total_document_count. +

                              Set the connector sync job stats.

                              +

                              Stats include: deleted_document_count, indexed_document_count, indexed_document_volume, and total_document_count. You can also update last_seen. This API is mainly used by the connector service for updating sync job information.

                              To sync data using self-managed connectors, you need to deploy the Elastic connector service on your own infrastructure. @@ -1403,8 +1403,8 @@ def update_features( """ .. raw:: html -

                              Update the connector features. - Update the connector features in the connector document. +

                              Update the connector features.

                              +

                              Update the connector features in the connector document. This API can be used to control the following aspects of a connector:

                              • document-level security
                              • diff --git a/elasticsearch/_sync/client/dangling_indices.py b/elasticsearch/_sync/client/dangling_indices.py index b40de169e..b547a0020 100644 --- a/elasticsearch/_sync/client/dangling_indices.py +++ b/elasticsearch/_sync/client/dangling_indices.py @@ -30,7 +30,7 @@ def delete_dangling_index( self, *, index_uuid: str, - accept_data_loss: bool, + accept_data_loss: t.Optional[bool] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, @@ -41,8 +41,8 @@ def delete_dangling_index( """ .. raw:: html -

                                Delete a dangling index. - If Elasticsearch encounters index data that is absent from the current cluster state, those indices are considered to be dangling. +

                                Delete a dangling index.

                                +

                                If Elasticsearch encounters index data that is absent from the current cluster state, those indices are considered to be dangling. For example, this can happen if you delete more than cluster.indices.tombstones.size indices while an Elasticsearch node is offline.

                                @@ -52,13 +52,11 @@ def delete_dangling_index( API to find the UUID. :param accept_data_loss: This parameter must be set to true to acknowledge that it will no longer be possible to recove data from the dangling index. - :param master_timeout: Specify timeout for connection to master - :param timeout: Explicit operation timeout + :param master_timeout: The period to wait for a connection to the master node. + :param timeout: The period to wait for a response. """ if index_uuid in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'index_uuid'") - if accept_data_loss is None: - raise ValueError("Empty value passed for parameter 'accept_data_loss'") __path_parts: t.Dict[str, str] = {"index_uuid": _quote(index_uuid)} __path = f'/_dangling/{__path_parts["index_uuid"]}' __query: t.Dict[str, t.Any] = {} @@ -91,7 +89,7 @@ def import_dangling_index( self, *, index_uuid: str, - accept_data_loss: bool, + accept_data_loss: t.Optional[bool] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, @@ -116,13 +114,11 @@ def import_dangling_index( from or determine which shard copies are fresh and which are stale, it cannot guarantee that the imported data represents the latest state of the index when it was last in the cluster. - :param master_timeout: Specify timeout for connection to master - :param timeout: Explicit operation timeout + :param master_timeout: The period to wait for a connection to the master node. + :param timeout: The period to wait for a response. """ if index_uuid in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'index_uuid'") - if accept_data_loss is None: - raise ValueError("Empty value passed for parameter 'accept_data_loss'") __path_parts: t.Dict[str, str] = {"index_uuid": _quote(index_uuid)} __path = f'/_dangling/{__path_parts["index_uuid"]}' __query: t.Dict[str, t.Any] = {} diff --git a/elasticsearch/_sync/client/enrich.py b/elasticsearch/_sync/client/enrich.py index a12372552..70374133e 100644 --- a/elasticsearch/_sync/client/enrich.py +++ b/elasticsearch/_sync/client/enrich.py @@ -39,8 +39,8 @@ def delete_policy( """ .. raw:: html -

                                Delete an enrich policy. - Deletes an existing enrich policy and its enrich index.

                                +

                                Delete an enrich policy.

                                +

                                Deletes an existing enrich policy and its enrich index.

                                ``_ @@ -88,8 +88,8 @@ def execute_policy( """ .. raw:: html -

                                Run an enrich policy. - Create the enrich index for an existing enrich policy.

                                +

                                Run an enrich policy.

                                +

                                Create the enrich index for an existing enrich policy.

                                ``_ @@ -140,8 +140,8 @@ def get_policy( """ .. raw:: html -

                                Get an enrich policy. - Returns information about an enrich policy.

                                +

                                Get an enrich policy.

                                +

                                Returns information about an enrich policy.

                                ``_ @@ -198,8 +198,8 @@ def put_policy( """ .. raw:: html -

                                Create an enrich policy. - Creates an enrich policy.

                                +

                                Create an enrich policy.

                                +

                                Creates an enrich policy.

                                ``_ @@ -259,8 +259,8 @@ def stats( """ .. raw:: html -

                                Get enrich stats. - Returns enrich coordinator statistics and information about enrich policies that are currently executing.

                                +

                                Get enrich stats.

                                +

                                Returns enrich coordinator statistics and information about enrich policies that are currently executing.

                                ``_ diff --git a/elasticsearch/_sync/client/eql.py b/elasticsearch/_sync/client/eql.py index aa53d77b9..92a3aba4e 100644 --- a/elasticsearch/_sync/client/eql.py +++ b/elasticsearch/_sync/client/eql.py @@ -38,8 +38,8 @@ def delete( """ .. raw:: html -

                                Delete an async EQL search. - Delete an async EQL search or a stored synchronous EQL search. +

                                Delete an async EQL search.

                                +

                                Delete an async EQL search or a stored synchronous EQL search. The API also deletes results for the search.

                                @@ -89,8 +89,8 @@ def get( """ .. raw:: html -

                                Get async EQL search results. - Get the current status and available results for an async EQL search or a stored synchronous EQL search.

                                +

                                Get async EQL search results.

                                +

                                Get the current status and available results for an async EQL search or a stored synchronous EQL search.

                                ``_ @@ -143,8 +143,8 @@ def get_status( """ .. raw:: html -

                                Get the async EQL status. - Get the current status for an async EQL search or a stored synchronous EQL search without returning results.

                                +

                                Get the async EQL status.

                                +

                                Get the current status for an async EQL search or a stored synchronous EQL search without returning results.

                                ``_ @@ -243,14 +243,14 @@ def search( """ .. raw:: html -

                                Get EQL search results. - Returns search results for an Event Query Language (EQL) query. +

                                Get EQL search results.

                                +

                                Returns search results for an Event Query Language (EQL) query. EQL assumes each document in a data stream or index corresponds to an event.

                                ``_ - :param index: The name of the index to scope the operation + :param index: Comma-separated list of index names to scope the operation :param query: EQL query you wish to run. :param allow_no_indices: Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices @@ -296,7 +296,7 @@ def search( Defaults to 10 :param tiebreaker_field: Field used to sort hits with the same timestamp in ascending order - :param timestamp_field: Field containing event timestamp. Default "@timestamp" + :param timestamp_field: Field containing event timestamp. :param wait_for_completion_timeout: """ if index in SKIP_IN_PATH: diff --git a/elasticsearch/_sync/client/esql.py b/elasticsearch/_sync/client/esql.py index ae42d4f69..05d0f3e43 100644 --- a/elasticsearch/_sync/client/esql.py +++ b/elasticsearch/_sync/client/esql.py @@ -90,8 +90,8 @@ def async_query( """ .. raw:: html -

                                Run an async ES|QL query. - Asynchronously run an ES|QL (Elasticsearch query language) query, monitor its progress, and retrieve results when they become available.

                                +

                                Run an async ES|QL query.

                                +

                                Asynchronously run an ES|QL (Elasticsearch query language) query, monitor its progress, and retrieve results when they become available.

                                The API accepts the same parameters and request body as the synchronous query API, along with additional async related properties.

                                @@ -226,8 +226,8 @@ def async_query_delete( """ .. raw:: html -

                                Delete an async ES|QL query. - If the query is still running, it is cancelled. +

                                Delete an async ES|QL query.

                                +

                                If the query is still running, it is cancelled. Otherwise, the stored results are deleted.

                                If the Elasticsearch security features are enabled, only the following users can use this API to delete a query:

                                  @@ -292,8 +292,8 @@ def async_query_get( """ .. raw:: html -

                                  Get async ES|QL query results. - Get the current status and available results or stored results for an ES|QL asynchronous query. +

                                  Get async ES|QL query results.

                                  +

                                  Get the current status and available results or stored results for an ES|QL asynchronous query. If the Elasticsearch security features are enabled, only the user who first submitted the ES|QL query can retrieve the results using this API.

                                  @@ -417,8 +417,8 @@ def get_query( """ .. raw:: html -

                                  Get a specific running ES|QL query information. - Returns an object extended information about a running ES|QL query.

                                  +

                                  Get a specific running ES|QL query information.

                                  +

                                  Returns an object extended information about a running ES|QL query.

                                  ``_ @@ -461,8 +461,8 @@ def list_queries( """ .. raw:: html -

                                  Get running ES|QL queries information. - Returns an object containing IDs and other information about the running ES|QL queries.

                                  +

                                  Get running ES|QL queries information.

                                  +

                                  Returns an object containing IDs and other information about the running ES|QL queries.

                                  ``_ @@ -543,8 +543,8 @@ def query( """ .. raw:: html -

                                  Run an ES|QL query. - Get search results for an ES|QL (Elasticsearch query language) query.

                                  +

                                  Run an ES|QL query.

                                  +

                                  Get search results for an ES|QL (Elasticsearch query language) query.

                                  ``_ diff --git a/elasticsearch/_sync/client/features.py b/elasticsearch/_sync/client/features.py index 3107d6052..c13eb401f 100644 --- a/elasticsearch/_sync/client/features.py +++ b/elasticsearch/_sync/client/features.py @@ -38,8 +38,8 @@ def get_features( """ .. raw:: html -

                                  Get the features. - Get a list of features that can be included in snapshots using the feature_states field when creating a snapshot. +

                                  Get the features.

                                  +

                                  Get a list of features that can be included in snapshots using the feature_states field when creating a snapshot. You can use this API to determine which feature states to include when taking a snapshot. By default, all feature states are included in a snapshot if that snapshot includes the global state, or none if it does not.

                                  A feature state includes one or more system indices necessary for a given feature to function. @@ -89,8 +89,8 @@ def reset_features( """ .. raw:: html -

                                  Reset the features. - Clear all of the state information stored in system indices by Elasticsearch features, including the security and machine learning indices.

                                  +

                                  Reset the features.

                                  +

                                  Clear all of the state information stored in system indices by Elasticsearch features, including the security and machine learning indices.

                                  WARNING: Intended for development and testing use only. Do not reset features on a production cluster.

                                  Return a cluster to the same state as a new installation by resetting the feature state for all Elasticsearch features. This deletes all state information stored in system indices.

                                  diff --git a/elasticsearch/_sync/client/fleet.py b/elasticsearch/_sync/client/fleet.py index 5b0096f81..4b36d83c2 100644 --- a/elasticsearch/_sync/client/fleet.py +++ b/elasticsearch/_sync/client/fleet.py @@ -138,8 +138,8 @@ def msearch( """ .. raw:: html -

                                  Run multiple Fleet searches. - Run several Fleet searches with a single API request. +

                                  Run multiple Fleet searches.

                                  +

                                  Run several Fleet searches with a single API request. The API follows the same structure as the multi search API. However, similar to the Fleet search API, it supports the wait_for_checkpoints parameter.

                                  @@ -388,8 +388,8 @@ def search( """ .. raw:: html -

                                  Run a Fleet search. - The purpose of the Fleet search API is to provide an API where the search will be run only +

                                  Run a Fleet search.

                                  +

                                  The purpose of the Fleet search API is to provide an API where the search will be run only after the provided checkpoint has been processed and is visible for searches inside of Elasticsearch.

                                  diff --git a/elasticsearch/_sync/client/graph.py b/elasticsearch/_sync/client/graph.py index 7b6543e05..fc7512530 100644 --- a/elasticsearch/_sync/client/graph.py +++ b/elasticsearch/_sync/client/graph.py @@ -47,8 +47,8 @@ def explore( """ .. raw:: html -

                                  Explore graph analytics. - Extract and summarize information about the documents and terms in an Elasticsearch data stream or index. +

                                  Explore graph analytics.

                                  +

                                  Extract and summarize information about the documents and terms in an Elasticsearch data stream or index. The easiest way to understand the behavior of this API is to use the Graph UI to explore connections. An initial request to the _explore API contains a seed query that identifies the documents of interest and specifies the fields that define the vertices and connections you want to include in the graph. Subsequent requests enable you to spider out from one more vertices of interest. diff --git a/elasticsearch/_sync/client/ilm.py b/elasticsearch/_sync/client/ilm.py index a61afea52..c845de530 100644 --- a/elasticsearch/_sync/client/ilm.py +++ b/elasticsearch/_sync/client/ilm.py @@ -40,8 +40,8 @@ def delete_lifecycle( """ .. raw:: html -

                                  Delete a lifecycle policy. - You cannot delete policies that are currently in use. If the policy is being used to manage any indices, the request fails and returns an error.

                                  +

                                  Delete a lifecycle policy.

                                  +

                                  You cannot delete policies that are currently in use. If the policy is being used to manage any indices, the request fails and returns an error.

                                  ``_ @@ -96,8 +96,8 @@ def explain_lifecycle( """ .. raw:: html -

                                  Explain the lifecycle state. - Get the current lifecycle status for one or more indices. +

                                  Explain the lifecycle state.

                                  +

                                  Get the current lifecycle status for one or more indices. For data streams, the API retrieves the current lifecycle status for the stream's backing indices.

                                  The response indicates when the index entered each lifecycle state, provides the definition of the running phase, and information about any failures.

                                  @@ -260,8 +260,8 @@ def migrate_to_data_tiers( """ .. raw:: html -

                                  Migrate to data tiers routing. - Switch the indices, ILM policies, and legacy, composable, and component templates from using custom node attributes and attribute-based allocation filters to using data tiers. +

                                  Migrate to data tiers routing.

                                  +

                                  Switch the indices, ILM policies, and legacy, composable, and component templates from using custom node attributes and attribute-based allocation filters to using data tiers. Optionally, delete one legacy index template. Using node roles enables ILM to automatically move the indices between data tiers.

                                  Migrating away from custom node attributes routing can be manually performed. @@ -341,8 +341,8 @@ def move_to_step( """ .. raw:: html -

                                  Move to a lifecycle step. - Manually move an index into a specific step in the lifecycle policy and run that step.

                                  +

                                  Move to a lifecycle step.

                                  +

                                  Manually move an index into a specific step in the lifecycle policy and run that step.

                                  WARNING: This operation can result in the loss of data. Manually moving an index into a specific step runs that step even if it has already been performed. This is a potentially destructive action and this should be considered an expert level API.

                                  You must specify both the current step and the step to be executed in the body of the request. The request will fail if the current step does not match the step currently running for the index @@ -413,8 +413,8 @@ def put_lifecycle( """ .. raw:: html -

                                  Create or update a lifecycle policy. - If the specified policy exists, it is replaced and the policy version is incremented.

                                  +

                                  Create or update a lifecycle policy.

                                  +

                                  If the specified policy exists, it is replaced and the policy version is incremented.

                                  NOTE: Only the latest version of the policy is stored, you cannot revert to previous versions.

                                  @@ -473,8 +473,8 @@ def remove_policy( """ .. raw:: html -

                                  Remove policies from an index. - Remove the assigned lifecycle policies from an index or a data stream's backing indices. +

                                  Remove policies from an index.

                                  +

                                  Remove the assigned lifecycle policies from an index or a data stream's backing indices. It also stops managing the indices.

                                  @@ -518,8 +518,8 @@ def retry( """ .. raw:: html -

                                  Retry a policy. - Retry running the lifecycle policy for an index that is in the ERROR step. +

                                  Retry a policy.

                                  +

                                  Retry running the lifecycle policy for an index that is in the ERROR step. The API sets the policy back to the step where the error occurred and runs the step. Use the explain lifecycle state API to determine whether an index is in the ERROR step.

                                  @@ -566,8 +566,8 @@ def start( """ .. raw:: html -

                                  Start the ILM plugin. - Start the index lifecycle management plugin if it is currently stopped. +

                                  Start the ILM plugin.

                                  +

                                  Start the index lifecycle management plugin if it is currently stopped. ILM is started automatically when the cluster is formed. Restarting ILM is necessary only when it has been stopped using the stop ILM API.

                                  @@ -619,8 +619,8 @@ def stop( """ .. raw:: html -

                                  Stop the ILM plugin. - Halt all lifecycle management operations and stop the index lifecycle management plugin. +

                                  Stop the ILM plugin.

                                  +

                                  Halt all lifecycle management operations and stop the index lifecycle management plugin. This is useful when you are performing maintenance on the cluster and need to prevent ILM from performing any actions on your indices.

                                  The API returns as soon as the stop request has been acknowledged, but the plugin might continue to run until in-progress operations complete and the plugin can be safely stopped. Use the get ILM status API to check whether ILM is running.

                                  diff --git a/elasticsearch/_sync/client/indices.py b/elasticsearch/_sync/client/indices.py index 673dc6443..9cbcc9a27 100644 --- a/elasticsearch/_sync/client/indices.py +++ b/elasticsearch/_sync/client/indices.py @@ -165,8 +165,8 @@ def analyze( """ .. raw:: html -

                                  Get tokens from text analysis. - The analyze API performs analysis on a text string and returns the resulting tokens.

                                  +

                                  Get tokens from text analysis.

                                  +

                                  The analyze API performs analysis on a text string and returns the resulting tokens.

                                  Generating excessive amount of tokens may cause a node to run out of memory. The index.analyze.max_token_count setting enables you to limit the number of tokens that can be produced. If more than this limit of tokens gets generated, an error occurs. @@ -244,7 +244,6 @@ def analyze( ) @_rewrite_parameters() - @_availability_warning(Stability.EXPERIMENTAL) def cancel_migrate_reindex( self, *, @@ -315,8 +314,8 @@ def clear_cache( """ .. raw:: html -

                                  Clear the cache. - Clear the cache of one or more indices. +

                                  Clear the cache.

                                  +

                                  Clear the cache of one or more indices. For data streams, the API clears the caches of the stream's backing indices.

                                  By default, the clear cache API clears all caches. To clear only specific caches, use the fielddata, query, or request parameters. @@ -408,8 +407,8 @@ def clone( """ .. raw:: html -

                                  Clone an index. - Clone an existing index into a new index. +

                                  Clone an index.

                                  +

                                  Clone an existing index into a new index. Each original primary shard is cloned into a new primary shard in the new index.

                                  IMPORTANT: Elasticsearch does not apply index templates to the resulting index. The API also does not copy index metadata from the original index. @@ -533,8 +532,8 @@ def close( """ .. raw:: html -

                                  Close an index. - A closed index is blocked for read or write operations and does not allow all operations that opened indices allow. +

                                  Close an index.

                                  +

                                  A closed index is blocked for read or write operations and does not allow all operations that opened indices allow. It is not possible to index documents or to search for documents in a closed index. Closed indices do not have to maintain internal data structures for indexing or searching documents, which results in a smaller overhead on the cluster.

                                  When opening or closing an index, the master node is responsible for restarting the index shards to reflect the new state of the index. @@ -630,8 +629,8 @@ def create( """ .. raw:: html -

                                  Create an index. - You can use the create index API to add a new index to an Elasticsearch cluster. +

                                  Create an index.

                                  +

                                  You can use the create index API to add a new index to an Elasticsearch cluster. When creating an index, you can specify the following:

                                  • Settings for the index.
                                  • @@ -778,7 +777,6 @@ def create_data_stream( @_rewrite_parameters( body_name="create_from", ) - @_availability_warning(Stability.EXPERIMENTAL) def create_from( self, *, @@ -926,8 +924,8 @@ def delete( """ .. raw:: html -

                                    Delete indices. - Deleting an index deletes its documents, shards, and metadata. +

                                    Delete indices.

                                    +

                                    Deleting an index deletes its documents, shards, and metadata. It does not delete related Kibana components, such as data views, visualizations, or dashboards.

                                    You cannot delete the current write index of a data stream. To delete the index, you must roll over the data stream so a new write index is created. @@ -1004,8 +1002,8 @@ def delete_alias( """ .. raw:: html -

                                    Delete an alias. - Removes a data stream or index from an alias.

                                    +

                                    Delete an alias.

                                    +

                                    Removes a data stream or index from an alias.

                                    ``_ @@ -1072,18 +1070,18 @@ def delete_data_lifecycle( """ .. raw:: html -

                                    Delete data stream lifecycles. - Removes the data stream lifecycle from a data stream, rendering it not managed by the data stream lifecycle.

                                    +

                                    Delete data stream lifecycles.

                                    +

                                    Removes the data stream lifecycle from a data stream, rendering it not managed by the data stream lifecycle.

                                    ``_ :param name: A comma-separated list of data streams of which the data stream - lifecycle will be deleted; use `*` to get all data streams + lifecycle will be deleted. Use `*` to get all data streams :param expand_wildcards: Whether wildcard expressions should get expanded to open or closed indices (default: open) - :param master_timeout: Specify timeout for connection to master - :param timeout: Explicit timestamp for the document + :param master_timeout: The period to wait for a connection to the master node. + :param timeout: The period to wait for a response. """ if name in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'name'") @@ -1136,8 +1134,8 @@ def delete_data_stream( """ .. raw:: html -

                                    Delete data streams. - Deletes one or more data streams and their backing indices.

                                    +

                                    Delete data streams.

                                    +

                                    Deletes one or more data streams and their backing indices.

                                    ``_ @@ -1200,18 +1198,18 @@ def delete_data_stream_options( """ .. raw:: html -

                                    Delete data stream options. - Removes the data stream options from a data stream.

                                    +

                                    Delete data stream options.

                                    +

                                    Removes the data stream options from a data stream.

                                    ``_ :param name: A comma-separated list of data streams of which the data stream - options will be deleted; use `*` to get all data streams + options will be deleted. Use `*` to get all data streams :param expand_wildcards: Whether wildcard expressions should get expanded to - open or closed indices (default: open) - :param master_timeout: Specify timeout for connection to master - :param timeout: Explicit timestamp for the document + open or closed indices + :param master_timeout: The period to wait for a connection to the master node. + :param timeout: The period to wait for a response. """ if name in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'name'") @@ -1257,8 +1255,8 @@ def delete_index_template( """ .. raw:: html -

                                    Delete an index template. - The provided may contain multiple template names separated by a comma. If multiple template +

                                    Delete an index template.

                                    +

                                    The provided may contain multiple template names separated by a comma. If multiple template names are specified then there is no wildcard support and the provided names should match completely with existing templates.

                                    @@ -1315,8 +1313,8 @@ def delete_template( """ .. raw:: html -

                                    Delete a legacy index template. - IMPORTANT: This documentation is about legacy index templates, which are deprecated and will be replaced by the composable templates introduced in Elasticsearch 7.8.

                                    +

                                    Delete a legacy index template.

                                    +

                                    IMPORTANT: This documentation is about legacy index templates, which are deprecated and will be replaced by the composable templates introduced in Elasticsearch 7.8.

                                    ``_ @@ -1382,8 +1380,8 @@ def disk_usage( """ .. raw:: html -

                                    Analyze the index disk usage. - Analyze the disk usage of each field of an index or data stream. +

                                    Analyze the index disk usage.

                                    +

                                    Analyze the disk usage of each field of an index or data stream. This API might not support indices created in previous Elasticsearch versions. The result of a small index can be inaccurate as some parts of an index might not be analyzed by the API.

                                    NOTE: The total size of fields of the analyzed shards of the index in the response is usually smaller than the index store_size value because some small metadata files are ignored and some parts of data files might not be scanned by the API. @@ -1466,8 +1464,8 @@ def downsample( """ .. raw:: html -

                                    Downsample an index. - Aggregate a time series (TSDS) index and store pre-computed statistical summaries (min, max, sum, value_count and avg) for each metric field grouped by a configured time interval. +

                                    Downsample an index.

                                    +

                                    Aggregate a time series (TSDS) index and store pre-computed statistical summaries (min, max, sum, value_count and avg) for each metric field grouped by a configured time interval. For example, a TSDS index that contains metrics sampled every 10 seconds can be downsampled to an hourly index. All documents within an hour interval are summarized and stored as a single document in the downsample index.

                                    NOTE: Only indices in a time series data stream are supported. @@ -1543,8 +1541,8 @@ def exists( """ .. raw:: html -

                                    Check indices. - Check if one or more indices, index aliases, or data streams exist.

                                    +

                                    Check indices.

                                    +

                                    Check if one or more indices, index aliases, or data streams exist.

                                    ``_ @@ -1763,8 +1761,8 @@ def exists_template( """ .. raw:: html -

                                    Check existence of index templates. - Get information about whether index templates exist. +

                                    Check existence of index templates.

                                    +

                                    Get information about whether index templates exist. Index templates define settings, mappings, and aliases that can be applied automatically to new indices.

                                    IMPORTANT: This documentation is about legacy index templates, which are deprecated and will be replaced by the composable templates introduced in Elasticsearch 7.8.

                                    @@ -1823,16 +1821,16 @@ def explain_data_lifecycle( """ .. raw:: html -

                                    Get the status for a data stream lifecycle. - Get information about an index or data stream's current data stream lifecycle status, such as time since index creation, time since rollover, the lifecycle configuration managing the index, or any errors encountered during lifecycle execution.

                                    +

                                    Get the status for a data stream lifecycle.

                                    +

                                    Get information about an index or data stream's current data stream lifecycle status, such as time since index creation, time since rollover, the lifecycle configuration managing the index, or any errors encountered during lifecycle execution.

                                    ``_ - :param index: The name of the index to explain - :param include_defaults: indicates if the API should return the default values + :param index: Comma-separated list of index names to explain + :param include_defaults: Indicates if the API should return the default values the system uses for the index's lifecycle - :param master_timeout: Specify timeout for connection to master + :param master_timeout: The period to wait for a connection to the master node. """ if index in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'index'") @@ -1886,8 +1884,8 @@ def field_usage_stats( """ .. raw:: html -

                                    Get field usage stats. - Get field usage information for each shard and field of an index. +

                                    Get field usage stats.

                                    +

                                    Get field usage information for each shard and field of an index. Field usage statistics are automatically captured when queries are running on a cluster. A shard-level search request that accesses a given field, even if multiple times during that request, is counted as a single use.

                                    The response body reports the per-shard usage count of the data structures that back the fields in the index. @@ -1968,8 +1966,8 @@ def flush( """ .. raw:: html -

                                    Flush data streams or indices. - Flushing a data stream or index is the process of making sure that any data that is currently only stored in the transaction log is also permanently stored in the Lucene index. +

                                    Flush data streams or indices.

                                    +

                                    Flushing a data stream or index is the process of making sure that any data that is currently only stored in the transaction log is also permanently stored in the Lucene index. When restarting, Elasticsearch replays any unflushed operations from the transaction log into the Lucene index to bring it back into the state that it was in before the restart. Elasticsearch automatically triggers flushes as needed, using heuristics that trade off the size of the unflushed transaction log against the cost of performing each flush.

                                    After each operation has been flushed it is permanently stored in the Lucene index. @@ -2062,8 +2060,8 @@ def forcemerge( """ .. raw:: html -

                                    Force a merge. - Perform the force merge operation on the shards of one or more indices. +

                                    Force a merge.

                                    +

                                    Perform the force merge operation on the shards of one or more indices. For data streams, the API forces a merge on the shards of the stream's backing indices.

                                    Merging reduces the number of segments in each shard by merging some of them together and also frees up the space used by deleted documents. Merging normally happens automatically, but sometimes it is useful to trigger a merge manually.

                                    @@ -2114,15 +2112,15 @@ def forcemerge( :param expand_wildcards: Whether to expand wildcard expression to concrete indices that are open, closed or both. :param flush: Specify whether the index should be flushed after performing the - operation (default: true) + operation :param ignore_unavailable: Whether specified concrete indices should be ignored when unavailable (missing or closed) :param max_num_segments: The number of segments the index should be merged into - (default: dynamic) + (defayult: dynamic) :param only_expunge_deletes: Specify whether the operation should only expunge deleted documents :param wait_for_completion: Should the request wait until the force merge is - completed. + completed """ __path_parts: t.Dict[str, str] if index not in SKIP_IN_PATH: @@ -2197,8 +2195,8 @@ def get( """ .. raw:: html -

                                    Get index information. - Get information about one or more indices. For data streams, the API returns information about the +

                                    Get index information.

                                    +

                                    Get information about one or more indices. For data streams, the API returns information about the stream’s backing indices.

                                    @@ -2291,8 +2289,8 @@ def get_alias( """ .. raw:: html -

                                    Get aliases. - Retrieves information for one or more data stream or index aliases.

                                    +

                                    Get aliases.

                                    +

                                    Retrieves information for one or more data stream or index aliases.

                                    ``_ @@ -2435,8 +2433,8 @@ def get_data_lifecycle_stats( """ .. raw:: html -

                                    Get data stream lifecycle stats. - Get statistics about the data streams that are managed by a data stream lifecycle.

                                    +

                                    Get data stream lifecycle stats.

                                    +

                                    Get statistics about the data streams that are managed by a data stream lifecycle.

                                    ``_ @@ -2730,8 +2728,8 @@ def get_field_mapping( """ .. raw:: html -

                                    Get mapping definitions. - Retrieves mapping definitions for one or more fields. +

                                    Get mapping definitions.

                                    +

                                    Retrieves mapping definitions for one or more fields. For data streams, the API retrieves field mappings for the stream’s backing indices.

                                    This API is useful if you don't need a complete mapping or if an index mapping contains a large number of fields.

                                    @@ -2809,14 +2807,14 @@ def get_index_template( """ .. raw:: html -

                                    Get index templates. - Get information about one or more index templates.

                                    +

                                    Get index templates.

                                    +

                                    Get information about one or more index templates.

                                    ``_ - :param name: Comma-separated list of index template names used to limit the request. - Wildcard (*) expressions are supported. + :param name: Name of index template to retrieve. Wildcard (*) expressions are + supported. :param flat_settings: If true, returns settings in flat format. :param include_defaults: If true, returns all relevant default configurations for the index template. @@ -2886,8 +2884,8 @@ def get_mapping( """ .. raw:: html -

                                    Get mapping definitions. - For data streams, the API retrieves mappings for the stream’s backing indices.

                                    +

                                    Get mapping definitions.

                                    +

                                    For data streams, the API retrieves mappings for the stream’s backing indices.

                                    ``_ @@ -2947,7 +2945,6 @@ def get_mapping( ) @_rewrite_parameters() - @_availability_warning(Stability.EXPERIMENTAL) def get_migrate_reindex_status( self, *, @@ -3019,8 +3016,8 @@ def get_settings( """ .. raw:: html -

                                    Get index settings. - Get setting information for one or more indices. +

                                    Get index settings.

                                    +

                                    Get setting information for one or more indices. For data streams, it returns setting information for the stream's backing indices.

                                    @@ -3111,8 +3108,8 @@ def get_template( """ .. raw:: html -

                                    Get legacy index templates. - Get information about one or more index templates.

                                    +

                                    Get legacy index templates.

                                    +

                                    Get information about one or more index templates.

                                    IMPORTANT: This documentation is about legacy index templates, which are deprecated and will be replaced by the composable templates introduced in Elasticsearch 7.8.

                                    @@ -3163,7 +3160,6 @@ def get_template( @_rewrite_parameters( body_name="reindex", ) - @_availability_warning(Stability.EXPERIMENTAL) def migrate_reindex( self, *, @@ -3231,8 +3227,8 @@ def migrate_to_data_stream( """ .. raw:: html -

                                    Convert an index alias to a data stream. - Converts an index alias to a data stream. +

                                    Convert an index alias to a data stream.

                                    +

                                    Converts an index alias to a data stream. You must have a matching index template that is data stream enabled. The alias must meet the following criteria: The alias must have a write index; @@ -3296,8 +3292,8 @@ def modify_data_stream( """ .. raw:: html -

                                    Update data streams. - Performs one or more data stream modification actions in a single atomic operation.

                                    +

                                    Update data streams.

                                    +

                                    Performs one or more data stream modification actions in a single atomic operation.

                                    ``_ @@ -3360,8 +3356,8 @@ def open( """ .. raw:: html -

                                    Open a closed index. - For data streams, the API opens any closed backing indices.

                                    +

                                    Open a closed index.

                                    +

                                    For data streams, the API opens any closed backing indices.

                                    A closed index is blocked for read/write operations and does not allow all operations that opened indices allow. It is not possible to index documents or to search for documents in a closed index. This allows closed indices to not have to maintain internal data structures for indexing or searching documents, resulting in a smaller overhead on the cluster.

                                    @@ -3454,8 +3450,8 @@ def promote_data_stream( """ .. raw:: html -

                                    Promote a data stream. - Promote a data stream from a replicated data stream managed by cross-cluster replication (CCR) to a regular data stream.

                                    +

                                    Promote a data stream.

                                    +

                                    Promote a data stream from a replicated data stream managed by cross-cluster replication (CCR) to a regular data stream.

                                    With CCR auto following, a data stream from a remote cluster can be replicated to the local cluster. These data streams can't be rolled over in the local cluster. These replicated data streams roll over only if the upstream data stream rolls over. @@ -3467,7 +3463,7 @@ def promote_data_stream( ``_ - :param name: The name of the data stream + :param name: The name of the data stream to promote :param master_timeout: Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. @@ -3527,8 +3523,8 @@ def put_alias( """ .. raw:: html -

                                    Create or update an alias. - Adds a data stream or index to an alias.

                                    +

                                    Create or update an alias.

                                    +

                                    Adds a data stream or index to an alias.

                                    ``_ @@ -3613,7 +3609,7 @@ def put_data_lifecycle( *, name: t.Union[str, t.Sequence[str]], data_retention: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, - downsampling: t.Optional[t.Mapping[str, t.Any]] = None, + downsampling: t.Optional[t.Sequence[t.Mapping[str, t.Any]]] = None, enabled: t.Optional[bool] = None, error_trace: t.Optional[bool] = None, expand_wildcards: t.Optional[ @@ -3634,8 +3630,8 @@ def put_data_lifecycle( """ .. raw:: html -

                                    Update data stream lifecycles. - Update the data stream lifecycle of the specified data streams.

                                    +

                                    Update data stream lifecycles.

                                    +

                                    Update the data stream lifecycle of the specified data streams.

                                    ``_ @@ -3801,8 +3797,8 @@ def put_data_stream_options( """ .. raw:: html -

                                    Update data stream options. - Update the data stream options of the specified data streams.

                                    +

                                    Update data stream options.

                                    +

                                    Update the data stream options of the specified data streams.

                                    ``_ @@ -3972,8 +3968,8 @@ def put_index_template( """ .. raw:: html -

                                    Create or update an index template. - Index templates define settings, mappings, and aliases that can be applied automatically to new indices.

                                    +

                                    Create or update an index template.

                                    +

                                    Index templates define settings, mappings, and aliases that can be applied automatically to new indices.

                                    Elasticsearch applies templates to new indices based on an wildcard pattern that matches the index name. Index templates are applied during data stream or index creation. For data streams, these settings and mappings are applied when the stream's backing indices are created. @@ -4004,7 +4000,7 @@ def put_index_template( via `actions.auto_create_index`. If set to `false`, then indices or data streams matching the template must always be explicitly created, and may never be automatically created. - :param cause: User defined reason for creating/updating the index template + :param cause: User defined reason for creating or updating the index template :param composed_of: An ordered list of component template names. Component templates are merged in the order specified, meaning that the last component template specified has the highest precedence. @@ -4156,8 +4152,8 @@ def put_mapping( """ .. raw:: html -

                                    Update field mappings. - Add new fields to an existing data stream or index. +

                                    Update field mappings.

                                    +

                                    Add new fields to an existing data stream or index. You can use the update mapping API to:

                                    • Add a new field to an existing index
                                    • @@ -4174,7 +4170,7 @@ def put_mapping( ``_ :param index: A comma-separated list of index names the mapping should be added - to (supports wildcards); use `_all` or omit to add the mapping on all indices. + to (supports wildcards). Use `_all` or omit to add the mapping on all indices. :param allow_no_indices: If `false`, the request returns an error if any wildcard expression, index alias, or `_all` value targets only missing or closed indices. This behavior applies even if the request targets other open indices. @@ -4301,8 +4297,8 @@ def put_settings( """ .. raw:: html -

                                      Update index settings. - Changes dynamic index settings in real time. +

                                      Update index settings.

                                      +

                                      Changes dynamic index settings in real time. For data streams, index setting changes are applied to all backing indices by default.

                                      To revert a setting to the default value, use a null value. The list of per-index settings that can be updated dynamically on live indices can be found in index settings documentation. @@ -4455,8 +4451,8 @@ def put_template( """ .. raw:: html -

                                      Create or update a legacy index template. - Index templates define settings, mappings, and aliases that can be applied automatically to new indices. +

                                      Create or update a legacy index template.

                                      +

                                      Index templates define settings, mappings, and aliases that can be applied automatically to new indices. Elasticsearch applies templates to new indices based on an index pattern that matches the index name.

                                      IMPORTANT: This documentation is about legacy index templates, which are deprecated and will be replaced by the composable templates introduced in Elasticsearch 7.8.

                                      Composable templates always take precedence over legacy templates. @@ -4476,7 +4472,7 @@ def put_template( :param name: The name of the template :param aliases: Aliases for the index. - :param cause: User defined reason for creating/updating the index template + :param cause: User defined reason for creating or updating the index template :param create: If true, this request cannot replace or update existing index templates. :param index_patterns: Array of wildcard expressions used to match the names @@ -4563,8 +4559,8 @@ def recovery( """ .. raw:: html -

                                      Get index recovery information. - Get information about ongoing and completed shard recoveries for one or more indices. +

                                      Get index recovery information.

                                      +

                                      Get information about ongoing and completed shard recoveries for one or more indices. For data streams, the API returns information for the stream's backing indices.

                                      All recoveries, whether ongoing or complete, are kept in the cluster state and may be reported on at any time.

                                      Shard recovery is the process of initializing a shard copy, such as restoring a primary shard from a snapshot or creating a replica shard from a primary shard. @@ -4661,8 +4657,8 @@ def refresh( """ .. raw:: html -

                                      Refresh an index. - A refresh makes recent operations performed on one or more indices available for search. +

                                      Refresh an index.

                                      +

                                      A refresh makes recent operations performed on one or more indices available for search. For data streams, the API runs the refresh operation on the stream’s backing indices.

                                      By default, Elasticsearch periodically refreshes indices every second, but only on indices that have received one search request or more in the last 30 seconds. You can change this default interval with the index.refresh_interval setting.

                                      @@ -4745,8 +4741,8 @@ def reload_search_analyzers( """ .. raw:: html -

                                      Reload search analyzers. - Reload an index's search analyzers and their resources. +

                                      Reload search analyzers.

                                      +

                                      Reload an index's search analyzers and their resources. For data streams, the API reloads search analyzers and resources for the stream's backing indices.

                                      IMPORTANT: After reloading the search analyzers you should clear the request cache to make sure it doesn't contain responses derived from the previous versions of the analyzer.

                                      You can use the reload search analyzers API to pick up changes to synonym files used in the synonym_graph or synonym token filter of a search analyzer. @@ -5071,8 +5067,8 @@ def resolve_index( """ .. raw:: html -

                                      Resolve indices. - Resolve the names and/or index patterns for indices, aliases, and data streams. +

                                      Resolve indices.

                                      +

                                      Resolve the names and/or index patterns for indices, aliases, and data streams. Multiple patterns and remote clusters are supported.

                                      @@ -5160,8 +5156,8 @@ def rollover( """ .. raw:: html -

                                      Roll over to a new index. - TIP: We recommend using the index lifecycle rollover action to automate rollovers. However, Serverless does not support Index Lifecycle Management (ILM), so don't use this approach in the Serverless context.

                                      +

                                      Roll over to a new index.

                                      +

                                      TIP: We recommend using the index lifecycle rollover action to automate rollovers. However, Serverless does not support Index Lifecycle Management (ILM), so don't use this approach in the Serverless context.

                                      The rollover API creates a new index for a data stream or index alias. The API behavior depends on the rollover target.

                                      Roll over a data stream

                                      @@ -5297,8 +5293,8 @@ def segments( """ .. raw:: html -

                                      Get index segments. - Get low-level information about the Lucene segments in index shards. +

                                      Get index segments.

                                      +

                                      Get low-level information about the Lucene segments in index shards. For data streams, the API returns information about the stream's backing indices.

                                      @@ -5378,8 +5374,8 @@ def shard_stores( """ .. raw:: html -

                                      Get index shard stores. - Get store information about replica shards in one or more indices. +

                                      Get index shard stores.

                                      +

                                      Get store information about replica shards in one or more indices. For data streams, the API retrieves store information for the stream's backing indices.

                                      The index shard stores API returns the following information:

                                        @@ -5462,8 +5458,8 @@ def shrink( """ .. raw:: html -

                                        Shrink an index. - Shrink an index into a new index with fewer primary shards.

                                        +

                                        Shrink an index.

                                        +

                                        Shrink an index into a new index with fewer primary shards.

                                        Before you can shrink an index:

                                        • The index must be read-only.
                                        • @@ -5570,8 +5566,8 @@ def simulate_index_template( """ .. raw:: html -

                                          Simulate an index. - Get the index configuration that would be applied to the specified index from an existing index template.

                                          +

                                          Simulate an index.

                                          +

                                          Get the index configuration that would be applied to the specified index from an existing index template.

                                          ``_ @@ -5669,8 +5665,8 @@ def simulate_template( """ .. raw:: html -

                                          Simulate an index template. - Get the index configuration that would be applied by a particular index template.

                                          +

                                          Simulate an index template.

                                          +

                                          Get the index configuration that would be applied by a particular index template.

                                          ``_ @@ -5808,8 +5804,8 @@ def split( """ .. raw:: html -

                                          Split an index. - Split an index into a new index with more primary shards.

                                          +

                                          Split an index.

                                          +

                                          Split an index into a new index with more primary shards.

                                          • Before you can split an index:

                                            @@ -5933,8 +5929,8 @@ def stats( """ .. raw:: html -

                                            Get index statistics. - For data streams, the API retrieves statistics for the stream's backing indices.

                                            +

                                            Get index statistics.

                                            +

                                            For data streams, the API retrieves statistics for the stream's backing indices.

                                            By default, the returned statistics are index-level with primaries and total aggregations. primaries are the values for only the primary shards. total are the accumulated values for both primary and replica shards.

                                            @@ -5947,7 +5943,7 @@ def stats( :param index: A comma-separated list of index names; use `_all` or empty string to perform the operation on all indices - :param metric: Limit the information returned the specific metrics. + :param metric: Limit the information returned the specific metrics :param completion_fields: Comma-separated list or wildcard expressions of fields to include in fielddata and suggest statistics. :param expand_wildcards: Type of index that wildcard patterns can match. If the @@ -6038,8 +6034,8 @@ def update_aliases( """ .. raw:: html -

                                            Create or update an alias. - Adds a data stream or index to an alias.

                                            +

                                            Create or update an alias.

                                            +

                                            Adds a data stream or index to an alias.

                                            ``_ @@ -6117,8 +6113,8 @@ def validate_query( """ .. raw:: html -

                                            Validate a query. - Validates a query without running it.

                                            +

                                            Validate a query.

                                            +

                                            Validates a query without running it.

                                            ``_ diff --git a/elasticsearch/_sync/client/inference.py b/elasticsearch/_sync/client/inference.py index 8c906f86c..b342cfaa8 100644 --- a/elasticsearch/_sync/client/inference.py +++ b/elasticsearch/_sync/client/inference.py @@ -44,14 +44,16 @@ def completion( """ .. raw:: html -

                                            Perform completion inference on the service

                                            +

                                            Perform completion inference on the service.

                                            ``_ :param inference_id: The inference Id :param input: Inference input. Either a string or an array of strings. - :param task_settings: Optional task settings + :param task_settings: Task settings for the individual inference request. These + settings are specific to the you specified and override the task + settings specified when initializing the service. :param timeout: Specifies the amount of time to wait for the inference request to complete. """ @@ -116,15 +118,17 @@ def delete( """ .. raw:: html -

                                            Delete an inference endpoint

                                            +

                                            Delete an inference endpoint.

                                            +

                                            This API requires the manage_inference cluster privilege (the built-in inference_admin role grants this privilege).

                                            ``_ :param inference_id: The inference identifier. :param task_type: The task type - :param dry_run: When true, the endpoint is not deleted and a list of ingest processors - which reference this endpoint is returned. + :param dry_run: When true, checks the semantic_text fields and inference processors + that reference the endpoint and returns them in a list, but does not delete + the endpoint. :param force: When true, the inference endpoint is forcefully deleted even if it is still being used by ingest processors or semantic text fields. """ @@ -190,7 +194,8 @@ def get( """ .. raw:: html -

                                            Get an inference endpoint

                                            +

                                            Get an inference endpoint.

                                            +

                                            This API requires the monitor_inference cluster privilege (the built-in inference_admin and inference_user roles grant this privilege).

                                            ``_ @@ -544,7 +549,7 @@ def put_alibabacloud( self, *, task_type: t.Union[ - str, t.Literal["completion", "rerank", "space_embedding", "text_embedding"] + str, t.Literal["completion", "rerank", "sparse_embedding", "text_embedding"] ], alibabacloud_inference_id: str, service: t.Optional[t.Union[str, t.Literal["alibabacloud-ai-search"]]] = None, @@ -573,7 +578,9 @@ def put_alibabacloud( this case, `alibabacloud-ai-search`. :param service_settings: Settings used to install the inference model. These settings are specific to the `alibabacloud-ai-search` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `sparse_embedding` or `text_embedding` task types. Not applicable to + the `rerank` or `completion` task types. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -669,7 +676,8 @@ def put_amazonbedrock( this case, `amazonbedrock`. :param service_settings: Settings used to install the inference model. These settings are specific to the `amazonbedrock` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `completion` task type. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -771,7 +779,9 @@ def put_amazonsagemaker( :param service_settings: Settings used to install the inference model. These settings are specific to the `amazon_sagemaker` service and `service_settings.api` you specified. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `sparse_embedding` or `text_embedding` task types. Not applicable to + the `rerank`, `completion`, or `chat_completion` task types. :param task_settings: Settings to configure the inference task. These settings are specific to the task type and `service_settings.api` you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -825,12 +835,7 @@ def put_amazonsagemaker( ) @_rewrite_parameters( - body_fields=( - "service", - "service_settings", - "chunking_settings", - "task_settings", - ), + body_fields=("service", "service_settings", "task_settings"), ) def put_anthropic( self, @@ -839,7 +844,6 @@ def put_anthropic( anthropic_inference_id: str, service: t.Optional[t.Union[str, t.Literal["anthropic"]]] = None, service_settings: t.Optional[t.Mapping[str, t.Any]] = None, - chunking_settings: t.Optional[t.Mapping[str, t.Any]] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, @@ -863,8 +867,7 @@ def put_anthropic( :param service: The type of service supported for the specified task type. In this case, `anthropic`. :param service_settings: Settings used to install the inference model. These - settings are specific to the `watsonxai` service. - :param chunking_settings: The chunking configuration object. + settings are specific to the `anthropic` service. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -902,8 +905,6 @@ def put_anthropic( __body["service"] = service if service_settings is not None: __body["service_settings"] = service_settings - if chunking_settings is not None: - __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings __headers = {"accept": "application/json", "content-type": "application/json"} @@ -955,8 +956,10 @@ def put_azureaistudio( :param service: The type of service supported for the specified task type. In this case, `azureaistudio`. :param service_settings: Settings used to install the inference model. These - settings are specific to the `openai` service. - :param chunking_settings: The chunking configuration object. + settings are specific to the `azureaistudio` service. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `rerank` or `completion` + task types. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -1056,7 +1059,8 @@ def put_azureopenai( this case, `azureopenai`. :param service_settings: Settings used to install the inference model. These settings are specific to the `azureopenai` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `completion` task type. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -1148,7 +1152,9 @@ def put_cohere( this case, `cohere`. :param service_settings: Settings used to install the inference model. These settings are specific to the `cohere` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `rerank` or `completion` + task type. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -1200,12 +1206,7 @@ def put_cohere( ) @_rewrite_parameters( - body_fields=( - "service", - "service_settings", - "chunking_settings", - "task_settings", - ), + body_fields=("service", "service_settings", "task_settings"), ) def put_contextualai( self, @@ -1214,7 +1215,6 @@ def put_contextualai( contextualai_inference_id: str, service: t.Optional[t.Union[str, t.Literal["contextualai"]]] = None, service_settings: t.Optional[t.Mapping[str, t.Any]] = None, - chunking_settings: t.Optional[t.Mapping[str, t.Any]] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, @@ -1239,7 +1239,6 @@ def put_contextualai( this case, `contextualai`. :param service_settings: Settings used to install the inference model. These settings are specific to the `contextualai` service. - :param chunking_settings: The chunking configuration object. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -1277,8 +1276,6 @@ def put_contextualai( __body["service"] = service if service_settings is not None: __body["service_settings"] = service_settings - if chunking_settings is not None: - __body["chunking_settings"] = chunking_settings if task_settings is not None: __body["task_settings"] = task_settings __headers = {"accept": "application/json", "content-type": "application/json"} @@ -1372,7 +1369,9 @@ def put_custom( this case, `custom`. :param service_settings: Settings used to install the inference model. These settings are specific to the `custom` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `sparse_embedding` or `text_embedding` task types. Not applicable to + the `rerank` or `completion` task types. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. """ @@ -1420,7 +1419,7 @@ def put_custom( ) @_rewrite_parameters( - body_fields=("service", "service_settings", "chunking_settings"), + body_fields=("service", "service_settings"), ) def put_deepseek( self, @@ -1429,7 +1428,6 @@ def put_deepseek( deepseek_inference_id: str, service: t.Optional[t.Union[str, t.Literal["deepseek"]]] = None, service_settings: t.Optional[t.Mapping[str, t.Any]] = None, - chunking_settings: t.Optional[t.Mapping[str, t.Any]] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, @@ -1452,7 +1450,6 @@ def put_deepseek( this case, `deepseek`. :param service_settings: Settings used to install the inference model. These settings are specific to the `deepseek` service. - :param chunking_settings: The chunking configuration object. :param timeout: Specifies the amount of time to wait for the inference endpoint to be created. """ @@ -1486,8 +1483,6 @@ def put_deepseek( __body["service"] = service if service_settings is not None: __body["service_settings"] = service_settings - if chunking_settings is not None: - __body["chunking_settings"] = chunking_settings __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", @@ -1554,7 +1549,9 @@ def put_elasticsearch( this case, `elasticsearch`. :param service_settings: Settings used to install the inference model. These settings are specific to the `elasticsearch` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `sparse_embedding` and `text_embedding` task types. Not applicable to + the `rerank` task type. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -1735,7 +1732,8 @@ def put_googleaistudio( this case, `googleaistudio`. :param service_settings: Settings used to install the inference model. These settings are specific to the `googleaistudio` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `completion` task type. :param timeout: Specifies the amount of time to wait for the inference endpoint to be created. """ @@ -1825,7 +1823,9 @@ def put_googlevertexai( this case, `googlevertexai`. :param service_settings: Settings used to install the inference model. These settings are specific to the `googlevertexai` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `rerank`, `completion`, + or `chat_completion` task types. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -1953,7 +1953,9 @@ def put_hugging_face( this case, `hugging_face`. :param service_settings: Settings used to install the inference model. These settings are specific to the `hugging_face` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `rerank`, `completion`, + or `chat_completion` task types. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -2047,7 +2049,8 @@ def put_jinaai( this case, `jinaai`. :param service_settings: Settings used to install the inference model. These settings are specific to the `jinaai` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `rerank` task type. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -2133,7 +2136,9 @@ def put_llama( this case, `llama`. :param service_settings: Settings used to install the inference model. These settings are specific to the `llama` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `completion` or `chat_completion` + task types. :param timeout: Specifies the amount of time to wait for the inference endpoint to be created. """ @@ -2215,7 +2220,9 @@ def put_mistral( this case, `mistral`. :param service_settings: Settings used to install the inference model. These settings are specific to the `mistral` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `completion` or `chat_completion` + task types. :param timeout: Specifies the amount of time to wait for the inference endpoint to be created. """ @@ -2305,7 +2312,9 @@ def put_openai( this case, `openai`. :param service_settings: Settings used to install the inference model. These settings are specific to the `openai` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `completion` or `chat_completion` + task types. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -2396,7 +2405,8 @@ def put_voyageai( this case, `voyageai`. :param service_settings: Settings used to install the inference model. These settings are specific to the `voyageai` service. - :param chunking_settings: The chunking configuration object. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `rerank` task type. :param task_settings: Settings to configure the inference task. These settings are specific to the task type you specified. :param timeout: Specifies the amount of time to wait for the inference endpoint @@ -2448,7 +2458,7 @@ def put_voyageai( ) @_rewrite_parameters( - body_fields=("service", "service_settings"), + body_fields=("service", "service_settings", "chunking_settings"), ) def put_watsonx( self, @@ -2459,6 +2469,7 @@ def put_watsonx( watsonx_inference_id: str, service: t.Optional[t.Union[str, t.Literal["watsonxai"]]] = None, service_settings: t.Optional[t.Mapping[str, t.Any]] = None, + chunking_settings: t.Optional[t.Mapping[str, t.Any]] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, human: t.Optional[bool] = None, @@ -2483,6 +2494,9 @@ def put_watsonx( this case, `watsonxai`. :param service_settings: Settings used to install the inference model. These settings are specific to the `watsonxai` service. + :param chunking_settings: The chunking configuration object. Applies only to + the `text_embedding` task type. Not applicable to the `completion` or `chat_completion` + task types. :param timeout: Specifies the amount of time to wait for the inference endpoint to be created. """ @@ -2516,6 +2530,8 @@ def put_watsonx( __body["service"] = service if service_settings is not None: __body["service_settings"] = service_settings + if chunking_settings is not None: + __body["chunking_settings"] = chunking_settings __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "PUT", @@ -2547,7 +2563,7 @@ def rerank( """ .. raw:: html -

                                            Perform reranking inference on the service

                                            +

                                            Perform reranking inference on the service.

                                            ``_ @@ -2619,14 +2635,16 @@ def sparse_embedding( """ .. raw:: html -

                                            Perform sparse embedding inference on the service

                                            +

                                            Perform sparse embedding inference on the service.

                                            ``_ :param inference_id: The inference Id :param input: Inference input. Either a string or an array of strings. - :param task_settings: Optional task settings + :param task_settings: Task settings for the individual inference request. These + settings are specific to the you specified and override the task + settings specified when initializing the service. :param timeout: Specifies the amount of time to wait for the inference request to complete. """ @@ -2684,7 +2702,7 @@ def text_embedding( """ .. raw:: html -

                                            Perform text embedding inference on the service

                                            +

                                            Perform text embedding inference on the service.

                                            ``_ @@ -2698,7 +2716,9 @@ def text_embedding( to the relevant service-specific documentation for more info. > info > The `input_type` parameter specified on the root level of the request body will take precedence over the `input_type` parameter specified in `task_settings`. - :param task_settings: Optional task settings + :param task_settings: Task settings for the individual inference request. These + settings are specific to the you specified and override the task + settings specified when initializing the service. :param timeout: Specifies the amount of time to wait for the inference request to complete. """ diff --git a/elasticsearch/_sync/client/ingest.py b/elasticsearch/_sync/client/ingest.py index 1286d3627..97f6b9e2f 100644 --- a/elasticsearch/_sync/client/ingest.py +++ b/elasticsearch/_sync/client/ingest.py @@ -151,8 +151,8 @@ def delete_pipeline( """ .. raw:: html -

                                            Delete pipelines. - Delete one or more ingest pipelines.

                                            +

                                            Delete pipelines.

                                            +

                                            Delete one or more ingest pipelines.

                                            ``_ @@ -204,8 +204,8 @@ def geo_ip_stats( """ .. raw:: html -

                                            Get GeoIP statistics. - Get download statistics for GeoIP2 databases that are used with the GeoIP processor.

                                            +

                                            Get GeoIP statistics.

                                            +

                                            Get download statistics for GeoIP2 databases that are used with the GeoIP processor.

                                            ``_ @@ -355,7 +355,7 @@ def get_pipeline( :param master_timeout: Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. - :param summary: Return pipelines without their definitions (default: false) + :param summary: Return pipelines without their definitions """ __path_parts: t.Dict[str, str] if id not in SKIP_IN_PATH: @@ -399,8 +399,8 @@ def processor_grok( """ .. raw:: html -

                                            Run a grok processor. - Extract structured fields out of a single text field within a document. +

                                            Run a grok processor.

                                            +

                                            Extract structured fields out of a single text field within a document. You must choose which field to extract matched fields from, as well as the grok pattern you expect will match. A grok pattern is like a regular expression that supports aliased expressions that can be reused.

                                            @@ -613,8 +613,8 @@ def put_pipeline( """ .. raw:: html -

                                            Create or update a pipeline. - Changes made using this API take effect immediately.

                                            +

                                            Create or update a pipeline.

                                            +

                                            Changes made using this API take effect immediately.

                                            ``_ diff --git a/elasticsearch/_sync/client/license.py b/elasticsearch/_sync/client/license.py index d352d114c..728376ad9 100644 --- a/elasticsearch/_sync/client/license.py +++ b/elasticsearch/_sync/client/license.py @@ -312,8 +312,7 @@ def post_start_basic( ``_ - :param acknowledge: whether the user has acknowledged acknowledge messages (default: - false) + :param acknowledge: Whether the user has acknowledged acknowledge messages :param master_timeout: Period to wait for a connection to the master node. :param timeout: Period to wait for a response. If no response is received before the timeout expires, the request fails and returns an error. @@ -360,8 +359,8 @@ def post_start_trial( """ .. raw:: html -

                                            Start a trial. - Start a 30-day trial, which gives access to all subscription features.

                                            +

                                            Start a trial.

                                            +

                                            Start a 30-day trial, which gives access to all subscription features.

                                            NOTE: You are allowed to start a trial only if your cluster has not already activated a trial for the current major product version. For example, if you have already activated a trial for v8.0, you cannot start a new trial until v9.0. You can, however, request an extended trial at https://www.elastic.co/trialextension.

                                            To check the status of your trial, use the get trial status API.

                                            @@ -369,10 +368,9 @@ def post_start_trial( ``_ - :param acknowledge: whether the user has acknowledged acknowledge messages (default: - false) + :param acknowledge: Whether the user has acknowledged acknowledge messages :param master_timeout: Period to wait for a connection to the master node. - :param type: The type of trial license to generate (default: "trial") + :param type: The type of trial license to generate """ __path_parts: t.Dict[str, str] = {} __path = "/_license/start_trial" diff --git a/elasticsearch/_sync/client/logstash.py b/elasticsearch/_sync/client/logstash.py index ae8e2a1dc..1241a7d26 100644 --- a/elasticsearch/_sync/client/logstash.py +++ b/elasticsearch/_sync/client/logstash.py @@ -38,8 +38,8 @@ def delete_pipeline( """ .. raw:: html -

                                            Delete a Logstash pipeline. - Delete a pipeline that is used for Logstash Central Management. +

                                            Delete a Logstash pipeline.

                                            +

                                            Delete a pipeline that is used for Logstash Central Management. If the request succeeds, you receive an empty response with an appropriate status code.

                                            @@ -83,8 +83,8 @@ def get_pipeline( """ .. raw:: html -

                                            Get Logstash pipelines. - Get pipelines that are used for Logstash Central Management.

                                            +

                                            Get Logstash pipelines.

                                            +

                                            Get pipelines that are used for Logstash Central Management.

                                            ``_ diff --git a/elasticsearch/_sync/client/migration.py b/elasticsearch/_sync/client/migration.py index 20ebb44fd..1303f90fc 100644 --- a/elasticsearch/_sync/client/migration.py +++ b/elasticsearch/_sync/client/migration.py @@ -38,8 +38,8 @@ def deprecations( """ .. raw:: html -

                                            Get deprecation information. - Get information about different cluster, node, and index level settings that use deprecated features that will be removed or changed in the next major version.

                                            +

                                            Get deprecation information.

                                            +

                                            Get information about different cluster, node, and index level settings that use deprecated features that will be removed or changed in the next major version.

                                            TIP: This APIs is designed for indirect use by the Upgrade Assistant. You are strongly recommended to use the Upgrade Assistant.

                                            @@ -87,8 +87,8 @@ def get_feature_upgrade_status( """ .. raw:: html -

                                            Get feature migration information. - Version upgrades sometimes require changes to how features store configuration information and data in system indices. +

                                            Get feature migration information.

                                            +

                                            Version upgrades sometimes require changes to how features store configuration information and data in system indices. Check which features need to be migrated and the status of any migrations that are in progress.

                                            TIP: This API is designed for indirect use by the Upgrade Assistant. You are strongly recommended to use the Upgrade Assistant.

                                            @@ -129,8 +129,8 @@ def post_feature_upgrade( """ .. raw:: html -

                                            Start the feature migration. - Version upgrades sometimes require changes to how features store configuration information and data in system indices. +

                                            Start the feature migration.

                                            +

                                            Version upgrades sometimes require changes to how features store configuration information and data in system indices. This API starts the automatic migration process.

                                            Some functionality might be temporarily unavailable during the migration process.

                                            TIP: The API is designed for indirect use by the Upgrade Assistant. We strongly recommend you use the Upgrade Assistant.

                                            diff --git a/elasticsearch/_sync/client/ml.py b/elasticsearch/_sync/client/ml.py index 2bdc42926..66eea34a7 100644 --- a/elasticsearch/_sync/client/ml.py +++ b/elasticsearch/_sync/client/ml.py @@ -1108,8 +1108,8 @@ def flush_job( """ .. raw:: html -

                                            Force buffered data to be processed. - The flush jobs API is only applicable when sending data for analysis using +

                                            Force buffered data to be processed.

                                            +

                                            The flush jobs API is only applicable when sending data for analysis using the post data API. Depending on the content of the buffer, then it might additionally calculate new results. Both flush and close operations are similar, however the flush is more efficient if you are expecting to send @@ -1276,8 +1276,8 @@ def get_buckets( """ .. raw:: html -

                                            Get anomaly detection job results for buckets. - The API presents a chronological view of the records, grouped by bucket.

                                            +

                                            Get anomaly detection job results for buckets.

                                            +

                                            The API presents a chronological view of the records, grouped by bucket.

                                            ``_ @@ -1605,8 +1605,8 @@ def get_data_frame_analytics( """ .. raw:: html -

                                            Get data frame analytics job configuration info. - You can get information for multiple data frame analytics jobs in a single +

                                            Get data frame analytics job configuration info.

                                            +

                                            You can get information for multiple data frame analytics jobs in a single API request by using a comma-separated list of data frame analytics jobs or a wildcard expression.

                                            @@ -1751,8 +1751,8 @@ def get_datafeed_stats( """ .. raw:: html -

                                            Get datafeed stats. - You can get statistics for multiple datafeeds in a single API request by +

                                            Get datafeed stats.

                                            +

                                            You can get statistics for multiple datafeeds in a single API request by using a comma-separated list of datafeeds or a wildcard expression. You can get statistics for all datafeeds by using _all, by specifying * as the <feed_id>, or by omitting the <feed_id>. If the datafeed is stopped, the @@ -1816,8 +1816,8 @@ def get_datafeeds( """ .. raw:: html -

                                            Get datafeeds configuration info. - You can get information for multiple datafeeds in a single API request by +

                                            Get datafeeds configuration info.

                                            +

                                            You can get information for multiple datafeeds in a single API request by using a comma-separated list of datafeeds or a wildcard expression. You can get information for all datafeeds by using _all, by specifying * as the <feed_id>, or by omitting the <feed_id>. @@ -1887,8 +1887,8 @@ def get_filters( """ .. raw:: html -

                                            Get filters. - You can get a single filter or all filters.

                                            +

                                            Get filters.

                                            +

                                            You can get a single filter or all filters.

                                            ``_ @@ -1953,8 +1953,8 @@ def get_influencers( """ .. raw:: html -

                                            Get anomaly detection job results for influencers. - Influencers are the entities that have contributed to, or are to blame for, +

                                            Get anomaly detection job results for influencers.

                                            +

                                            Influencers are the entities that have contributed to, or are to blame for, the anomalies. Influencer results are available only if an influencer_field_name is specified in the job configuration.

                                            @@ -2100,8 +2100,8 @@ def get_jobs( """ .. raw:: html -

                                            Get anomaly detection jobs configuration info. - You can get information for multiple anomaly detection jobs in a single API +

                                            Get anomaly detection jobs configuration info.

                                            +

                                            You can get information for multiple anomaly detection jobs in a single API request by using a group name, a comma-separated list of jobs, or a wildcard expression. You can get information for all anomaly detection jobs by using _all, by specifying * as the <job_id>, or by omitting the <job_id>.

                                            @@ -2168,8 +2168,8 @@ def get_memory_stats( """ .. raw:: html -

                                            Get machine learning memory usage info. - Get information about how machine learning jobs and trained models are using memory, +

                                            Get machine learning memory usage info.

                                            +

                                            Get information about how machine learning jobs and trained models are using memory, on each node, both within the JVM heap, and natively, outside of the JVM.

                                            @@ -2522,8 +2522,8 @@ def get_records( """ .. raw:: html -

                                            Get anomaly records for an anomaly detection job. - Records contain the detailed analytical results. They describe the anomalous +

                                            Get anomaly records for an anomaly detection job.

                                            +

                                            Records contain the detailed analytical results. They describe the anomalous activity that has been identified in the input data based on the detector configuration. There can be many anomaly records depending on the characteristics and size @@ -2715,8 +2715,8 @@ def get_trained_models_stats( """ .. raw:: html -

                                            Get trained models usage info. - You can get usage information for multiple trained +

                                            Get trained models usage info.

                                            +

                                            You can get usage information for multiple trained models in a single API request by using a comma-separated list of model IDs or a wildcard expression.

                                            @@ -2843,8 +2843,8 @@ def info( """ .. raw:: html -

                                            Get machine learning information. - Get defaults and limits used by machine learning. +

                                            Get machine learning information.

                                            +

                                            Get defaults and limits used by machine learning. This endpoint is designed to be used by a user interface that needs to fully understand machine learning configurations where some options are not specified, meaning that the defaults should be used. This endpoint may be @@ -3052,10 +3052,7 @@ def post_data( if reset_start is not None: __query["reset_start"] = reset_start __body = data if data is not None else body - __headers = { - "accept": "application/json", - "content-type": "application/x-ndjson", - } + __headers = {"accept": "application/json", "content-type": "application/json"} return self.perform_request( # type: ignore[return-value] "POST", __path, @@ -3083,8 +3080,8 @@ def preview_data_frame_analytics( """ .. raw:: html -

                                            Preview features used by data frame analytics. - Preview the extracted features used by a data frame analytics config.

                                            +

                                            Preview features used by data frame analytics.

                                            +

                                            Preview the extracted features used by a data frame analytics config.

                                            ``_ @@ -3149,8 +3146,8 @@ def preview_datafeed( """ .. raw:: html -

                                            Preview a datafeed. - This API returns the first "page" of search results from a datafeed. +

                                            Preview a datafeed.

                                            +

                                            This API returns the first "page" of search results from a datafeed. You can preview an existing datafeed or provide configuration details for a datafeed and anomaly detection job in the API. The preview shows the structure of the data that will be passed to the anomaly detection engine. @@ -3371,8 +3368,8 @@ def put_data_frame_analytics( """ .. raw:: html -

                                            Create a data frame analytics job. - This API creates a data frame analytics job that performs an analysis on the +

                                            Create a data frame analytics job.

                                            +

                                            This API creates a data frame analytics job that performs an analysis on the source indices and stores the outcome in a destination index. By default, the query used in the source configuration is {"match_all": {}}.

                                            If the destination index does not exist, it is created automatically when you start the job.

                                            @@ -3552,8 +3549,8 @@ def put_datafeed( """ .. raw:: html -

                                            Create a datafeed. - Datafeeds retrieve data from Elasticsearch for analysis by an anomaly detection job. +

                                            Create a datafeed.

                                            +

                                            Datafeeds retrieve data from Elasticsearch for analysis by an anomaly detection job. You can associate only one datafeed with each anomaly detection job. The datafeed contains a query that runs at a defined interval (frequency). If you are concerned about delayed data, you can add a delay (query_delay') at each interval. By default, the datafeed uses the following query: {"match_all": {"boost": 1}}`.

                                            @@ -3721,8 +3718,8 @@ def put_filter( """ .. raw:: html -

                                            Create a filter. - A filter contains a list of strings. It can be used by one or more anomaly detection jobs. +

                                            Create a filter.

                                            +

                                            A filter contains a list of strings. It can be used by one or more anomaly detection jobs. Specifically, filters are referenced in the custom_rules property of detector configuration objects.

                                            @@ -4026,8 +4023,8 @@ def put_trained_model( """ .. raw:: html -

                                            Create a trained model. - Enable you to supply a trained model that is not created by data frame analytics.

                                            +

                                            Create a trained model.

                                            +

                                            Enable you to supply a trained model that is not created by data frame analytics.

                                            ``_ @@ -4132,8 +4129,8 @@ def put_trained_model_alias( """ .. raw:: html -

                                            Create or update a trained model alias. - A trained model alias is a logical name used to reference a single trained +

                                            Create or update a trained model alias.

                                            +

                                            A trained model alias is a logical name used to reference a single trained model. You can use aliases instead of trained model identifiers to make it easier to reference your models. For example, you can use aliases in inference @@ -4289,8 +4286,8 @@ def put_trained_model_vocabulary( """ .. raw:: html -

                                            Create a trained model vocabulary. - This API is supported only for natural language processing (NLP) models. +

                                            Create a trained model vocabulary.

                                            +

                                            This API is supported only for natural language processing (NLP) models. The vocabulary is stored in the index as described in inference_config.*.vocabulary of the trained model definition.

                                            @@ -4350,8 +4347,8 @@ def reset_job( """ .. raw:: html -

                                            Reset an anomaly detection job. - All model state and results are deleted. The job is ready to start over as if +

                                            Reset an anomaly detection job.

                                            +

                                            All model state and results are deleted. The job is ready to start over as if it had just been created. It is not currently possible to reset multiple jobs using wildcards or a comma separated list.

                                            @@ -4411,8 +4408,8 @@ def revert_model_snapshot( """ .. raw:: html -

                                            Revert to a snapshot. - The machine learning features react quickly to anomalous input, learning new +

                                            Revert to a snapshot.

                                            +

                                            The machine learning features react quickly to anomalous input, learning new behaviors in data. Highly anomalous input increases the variance in the models whilst the system learns whether this is a new step-change in behavior or a one-off event. In the case where this anomalous input is known to be a @@ -4481,8 +4478,8 @@ def set_upgrade_mode( """ .. raw:: html -

                                            Set upgrade_mode for ML indices. - Sets a cluster wide upgrade_mode setting that prepares machine learning +

                                            Set upgrade_mode for ML indices.

                                            +

                                            Sets a cluster wide upgrade_mode setting that prepares machine learning indices for an upgrade. When upgrading your cluster, in some circumstances you must restart your nodes and reindex your machine learning indices. In those circumstances, @@ -4528,7 +4525,9 @@ def set_upgrade_mode( path_parts=__path_parts, ) - @_rewrite_parameters() + @_rewrite_parameters( + body_fields=("timeout",), + ) def start_data_frame_analytics( self, *, @@ -4538,12 +4537,13 @@ def start_data_frame_analytics( human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + body: t.Optional[t.Dict[str, t.Any]] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html -

                                            Start a data frame analytics job. - A data frame analytics job can be started and stopped multiple times +

                                            Start a data frame analytics job.

                                            +

                                            A data frame analytics job can be started and stopped multiple times throughout its lifecycle. If the destination index does not exist, it is created automatically the first time you start the data frame analytics job. The @@ -4569,6 +4569,7 @@ def start_data_frame_analytics( __path_parts: t.Dict[str, str] = {"id": _quote(id)} __path = f'/_ml/data_frame/analytics/{__path_parts["id"]}/_start' __query: t.Dict[str, t.Any] = {} + __body: t.Dict[str, t.Any] = body if body is not None else {} if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -4577,14 +4578,20 @@ def start_data_frame_analytics( __query["human"] = human if pretty is not None: __query["pretty"] = pretty - if timeout is not None: - __query["timeout"] = timeout + if not __body: + if timeout is not None: + __body["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" return self.perform_request( # type: ignore[return-value] "POST", __path, params=__query, headers=__headers, + body=__body, endpoint_id="ml.start_data_frame_analytics", path_parts=__path_parts, ) @@ -4692,8 +4699,8 @@ def start_trained_model_deployment( """ .. raw:: html -

                                            Start a trained model deployment. - It allocates the model to every machine learning node.

                                            +

                                            Start a trained model deployment.

                                            +

                                            It allocates the model to every machine learning node.

                                            ``_ @@ -4714,7 +4721,7 @@ def start_trained_model_deployment( is greater than the number of hardware threads it will automatically be changed to a value less than the number of hardware threads. If adaptive_allocations is enabled, do not set this value, because it’s automatically set. - :param priority: The deployment priority. + :param priority: The deployment priority :param queue_capacity: Specifies the number of inference requests that are allowed in the queue. After the number of requests exceeds this value, new requests are rejected with a 429 error. @@ -4776,7 +4783,9 @@ def start_trained_model_deployment( path_parts=__path_parts, ) - @_rewrite_parameters() + @_rewrite_parameters( + body_fields=("allow_no_match", "force", "timeout"), + ) def stop_data_frame_analytics( self, *, @@ -4788,12 +4797,13 @@ def stop_data_frame_analytics( human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, + body: t.Optional[t.Dict[str, t.Any]] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html -

                                            Stop data frame analytics jobs. - A data frame analytics job can be started and stopped multiple times +

                                            Stop data frame analytics jobs.

                                            +

                                            A data frame analytics job can be started and stopped multiple times throughout its lifecycle.

                                            @@ -4819,26 +4829,33 @@ def stop_data_frame_analytics( __path_parts: t.Dict[str, str] = {"id": _quote(id)} __path = f'/_ml/data_frame/analytics/{__path_parts["id"]}/_stop' __query: t.Dict[str, t.Any] = {} - if allow_no_match is not None: - __query["allow_no_match"] = allow_no_match + __body: t.Dict[str, t.Any] = body if body is not None else {} if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: __query["filter_path"] = filter_path - if force is not None: - __query["force"] = force if human is not None: __query["human"] = human if pretty is not None: __query["pretty"] = pretty - if timeout is not None: - __query["timeout"] = timeout + if not __body: + if allow_no_match is not None: + __body["allow_no_match"] = allow_no_match + if force is not None: + __body["force"] = force + if timeout is not None: + __body["timeout"] = timeout + if not __body: + __body = None # type: ignore[assignment] __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" return self.perform_request( # type: ignore[return-value] "POST", __path, params=__query, headers=__headers, + body=__body, endpoint_id="ml.stop_data_frame_analytics", path_parts=__path_parts, ) @@ -4862,8 +4879,8 @@ def stop_datafeed( """ .. raw:: html -

                                            Stop datafeeds. - A datafeed that is stopped ceases to retrieve data from Elasticsearch. A datafeed can be started and stopped +

                                            Stop datafeeds.

                                            +

                                            A datafeed that is stopped ceases to retrieve data from Elasticsearch. A datafeed can be started and stopped multiple times throughout its lifecycle.

                                            @@ -4914,7 +4931,9 @@ def stop_datafeed( path_parts=__path_parts, ) - @_rewrite_parameters() + @_rewrite_parameters( + body_fields=("allow_no_match", "force", "id"), + ) def stop_trained_model_deployment( self, *, @@ -4924,7 +4943,9 @@ def stop_trained_model_deployment( filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, force: t.Optional[bool] = None, human: t.Optional[bool] = None, + id: t.Optional[str] = None, pretty: t.Optional[bool] = None, + body: t.Optional[t.Dict[str, t.Any]] = None, ) -> ObjectApiResponse[t.Any]: """ .. raw:: html @@ -4944,30 +4965,40 @@ def stop_trained_model_deployment( no matches or only partial matches. :param force: Forcefully stops the deployment, even if it is used by ingest pipelines. You can't use these pipelines until you restart the model deployment. + :param id: If provided, must be the same identifier as in the path. """ if model_id in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'model_id'") __path_parts: t.Dict[str, str] = {"model_id": _quote(model_id)} __path = f'/_ml/trained_models/{__path_parts["model_id"]}/deployment/_stop' __query: t.Dict[str, t.Any] = {} - if allow_no_match is not None: - __query["allow_no_match"] = allow_no_match + __body: t.Dict[str, t.Any] = body if body is not None else {} if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: __query["filter_path"] = filter_path - if force is not None: - __query["force"] = force if human is not None: __query["human"] = human if pretty is not None: __query["pretty"] = pretty + if not __body: + if allow_no_match is not None: + __body["allow_no_match"] = allow_no_match + if force is not None: + __body["force"] = force + if id is not None: + __body["id"] = id + if not __body: + __body = None # type: ignore[assignment] __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" return self.perform_request( # type: ignore[return-value] "POST", __path, params=__query, headers=__headers, + body=__body, endpoint_id="ml.stop_trained_model_deployment", path_parts=__path_parts, ) @@ -5108,8 +5139,8 @@ def update_datafeed( """ .. raw:: html -

                                            Update a datafeed. - You must stop and start the datafeed for the changes to be applied. +

                                            Update a datafeed.

                                            +

                                            You must stop and start the datafeed for the changes to be applied. When Elasticsearch security features are enabled, your datafeed remembers which roles the user who updated it had at the time of the update and runs the query using those same roles. If you provide secondary authorization headers, those credentials are used instead.

                                            @@ -5272,8 +5303,8 @@ def update_filter( """ .. raw:: html -

                                            Update a filter. - Updates the description of a filter, adds items, or removes items from the list.

                                            +

                                            Update a filter.

                                            +

                                            Updates the description of a filter, adds items, or removes items from the list.

                                            ``_ @@ -5366,8 +5397,8 @@ def update_job( """ .. raw:: html -

                                            Update an anomaly detection job. - Updates certain properties of an anomaly detection job.

                                            +

                                            Update an anomaly detection job.

                                            +

                                            Updates certain properties of an anomaly detection job.

                                            ``_ @@ -5498,8 +5529,8 @@ def update_model_snapshot( """ .. raw:: html -

                                            Update a snapshot. - Updates certain properties of a snapshot.

                                            +

                                            Update a snapshot.

                                            +

                                            Updates certain properties of a snapshot.

                                            ``_ @@ -5632,8 +5663,8 @@ def upgrade_job_snapshot( """ .. raw:: html -

                                            Upgrade a snapshot. - Upgrade an anomaly detection model snapshot to the latest major version. +

                                            Upgrade a snapshot.

                                            +

                                            Upgrade an anomaly detection model snapshot to the latest major version. Over time, older snapshot formats are deprecated and removed. Anomaly detection jobs support only snapshots that are from the current or previous major version. diff --git a/elasticsearch/_sync/client/monitoring.py b/elasticsearch/_sync/client/monitoring.py index daead423d..9973ef7c6 100644 --- a/elasticsearch/_sync/client/monitoring.py +++ b/elasticsearch/_sync/client/monitoring.py @@ -45,8 +45,8 @@ def bulk( """ .. raw:: html -

                                            Send monitoring data. - This API is used by the monitoring features to send monitoring data.

                                            +

                                            Send monitoring data.

                                            +

                                            This API is used by the monitoring features to send monitoring data.

                                            ``_ diff --git a/elasticsearch/_sync/client/nodes.py b/elasticsearch/_sync/client/nodes.py index 8a754d2f3..616531a24 100644 --- a/elasticsearch/_sync/client/nodes.py +++ b/elasticsearch/_sync/client/nodes.py @@ -46,8 +46,8 @@ def clear_repositories_metering_archive( """ .. raw:: html -

                                            Clear the archived repositories metering. - Clear the archived repositories metering information in the cluster.

                                            +

                                            Clear the archived repositories metering.

                                            +

                                            Clear the archived repositories metering information in the cluster.

                                            ``_ @@ -99,8 +99,8 @@ def get_repositories_metering_info( """ .. raw:: html -

                                            Get cluster repositories metering. - Get repositories metering information for a cluster. +

                                            Get cluster repositories metering.

                                            +

                                            Get repositories metering information for a cluster. This API exposes monotonically non-decreasing counters and it is expected that clients would durably store the information needed to compute aggregations over a period of time. Additionally, the information exposed by this API is volatile, meaning that it will not be present after node restarts.

                                            @@ -157,8 +157,8 @@ def hot_threads( """ .. raw:: html -

                                            Get the hot threads for nodes. - Get a breakdown of the hot threads on each selected node in the cluster. +

                                            Get the hot threads for nodes.

                                            +

                                            Get a breakdown of the hot threads on each selected node in the cluster. The output is plain text with a breakdown of the top hot threads for each node.

                                            @@ -169,7 +169,7 @@ def hot_threads( select, or to get a task from an empty queue) are filtered out. :param interval: The interval to do the second sampling of threads. :param snapshots: Number of samples of thread stacktrace. - :param sort: The sort order for 'cpu' type (default: total) + :param sort: The sort order for 'cpu' type :param threads: Specifies the number of hot threads to provide information for. :param timeout: Period to wait for a response. If no response is received before the timeout expires, the request fails and returns an error. @@ -376,8 +376,8 @@ def stats( """ .. raw:: html -

                                            Get node statistics. - Get statistics for nodes in a cluster. +

                                            Get node statistics.

                                            +

                                            Get statistics for nodes in a cluster. By default, all stats are returned. You can limit the returned information by using metrics.

                                            @@ -385,7 +385,7 @@ def stats( :param node_id: Comma-separated list of node IDs or names used to limit returned information. - :param metric: Limit the information returned to the specified metrics + :param metric: Limits the information returned to the specific metrics. :param index_metric: Limit the information returned for indices metric to the specific index metrics. It can be used only if indices (or all) metric is specified. @@ -499,8 +499,8 @@ def usage( ``_ :param node_id: A comma-separated list of node IDs or names to limit the returned - information; use `_local` to return information from the node you're connecting - to, leave empty to get information from all nodes + information. Use `_local` to return information from the node you're connecting + to, leave empty to get information from all nodes. :param metric: Limits the information returned to the specific metrics. A comma-separated list of the following options: `_all`, `rest_actions`. :param timeout: Period to wait for a response. If no response is received before diff --git a/elasticsearch/_sync/client/project.py b/elasticsearch/_sync/client/project.py index 04b3973cd..1764a0057 100644 --- a/elasticsearch/_sync/client/project.py +++ b/elasticsearch/_sync/client/project.py @@ -42,7 +42,8 @@ def tags( """ .. raw:: html -

                                            Return tags defined for the project

                                            +

                                            Get tags.

                                            +

                                            Get the tags that are defined for the project.

                                            """ __path_parts: t.Dict[str, str] = {} diff --git a/elasticsearch/_sync/client/query_rules.py b/elasticsearch/_sync/client/query_rules.py index 351c4dd80..9d5298da5 100644 --- a/elasticsearch/_sync/client/query_rules.py +++ b/elasticsearch/_sync/client/query_rules.py @@ -39,8 +39,8 @@ def delete_rule( """ .. raw:: html -

                                            Delete a query rule. - Delete a query rule within a query ruleset. +

                                            Delete a query rule.

                                            +

                                            Delete a query rule within a query ruleset. This is a destructive action that is only recoverable by re-adding the same rule with the create or update query rule API.

                                            @@ -92,8 +92,8 @@ def delete_ruleset( """ .. raw:: html -

                                            Delete a query ruleset. - Remove a query ruleset and its associated data. +

                                            Delete a query ruleset.

                                            +

                                            Remove a query ruleset and its associated data. This is a destructive action that is not recoverable.

                                            @@ -138,8 +138,8 @@ def get_rule( """ .. raw:: html -

                                            Get a query rule. - Get details about a query rule within a query ruleset.

                                            +

                                            Get a query rule.

                                            +

                                            Get details about a query rule within a query ruleset.

                                            ``_ @@ -190,8 +190,8 @@ def get_ruleset( """ .. raw:: html -

                                            Get a query ruleset. - Get details about a query ruleset.

                                            +

                                            Get a query ruleset.

                                            +

                                            Get details about a query ruleset.

                                            ``_ @@ -237,8 +237,8 @@ def list_rulesets( """ .. raw:: html -

                                            Get all query rulesets. - Get summarized information about the query rulesets.

                                            +

                                            Get all query rulesets.

                                            +

                                            Get summarized information about the query rulesets.

                                            ``_ @@ -294,8 +294,8 @@ def put_rule( """ .. raw:: html -

                                            Create or update a query rule. - Create or update a query rule within a query ruleset.

                                            +

                                            Create or update a query rule.

                                            +

                                            Create or update a query rule within a query ruleset.

                                            IMPORTANT: Due to limitations within pinned queries, you can only pin documents using ids or docs, but cannot use both in single rule. It is advised to use one or the other in query rulesets, to avoid errors. Additionally, pinned queries have a maximum limit of 100 pinned hits. @@ -380,8 +380,8 @@ def put_ruleset( """ .. raw:: html -

                                            Create or update a query ruleset. - There is a limit of 100 rules per ruleset. +

                                            Create or update a query ruleset.

                                            +

                                            There is a limit of 100 rules per ruleset. This limit can be increased by using the xpack.applications.rules.max_rules_per_ruleset cluster setting.

                                            IMPORTANT: Due to limitations within pinned queries, you can only select documents using ids or docs, but cannot use both in single rule. It is advised to use one or the other in query rulesets, to avoid errors. @@ -442,8 +442,8 @@ def test( """ .. raw:: html -

                                            Test a query ruleset. - Evaluate match criteria against a query ruleset to identify the rules that would match that criteria.

                                            +

                                            Test a query ruleset.

                                            +

                                            Evaluate match criteria against a query ruleset to identify the rules that would match that criteria.

                                            ``_ diff --git a/elasticsearch/_sync/client/rollup.py b/elasticsearch/_sync/client/rollup.py index 44b8b92ba..5aef61411 100644 --- a/elasticsearch/_sync/client/rollup.py +++ b/elasticsearch/_sync/client/rollup.py @@ -108,8 +108,8 @@ def get_jobs( """ .. raw:: html -

                                            Get rollup job information. - Get the configuration, stats, and status of rollup jobs.

                                            +

                                            Get rollup job information.

                                            +

                                            Get the configuration, stats, and status of rollup jobs.

                                            NOTE: This API returns only active (both STARTED and STOPPED) jobs. If a job was created, ran for a while, then was deleted, the API does not return any details about it. For details about a historical rollup job, the rollup capabilities API may be more useful.

                                            @@ -160,8 +160,8 @@ def get_rollup_caps( """ .. raw:: html -

                                            Get the rollup job capabilities. - Get the capabilities of any rollup jobs that have been configured for a specific index or index pattern.

                                            +

                                            Get the rollup job capabilities.

                                            +

                                            Get the capabilities of any rollup jobs that have been configured for a specific index or index pattern.

                                            This API is useful because a rollup job is often configured to rollup only a subset of fields from the source index. Furthermore, only certain aggregations can be configured for various fields, leading to a limited subset of functionality depending on that configuration. This API enables you to inspect an index and determine:

                                            @@ -216,8 +216,8 @@ def get_rollup_index_caps( """ .. raw:: html -

                                            Get the rollup index capabilities. - Get the rollup capabilities of all jobs inside of a rollup index. +

                                            Get the rollup index capabilities.

                                            +

                                            Get the rollup capabilities of all jobs inside of a rollup index. A single rollup index may store the data for multiple rollup jobs and may have a variety of capabilities depending on those jobs. This API enables you to determine:

                                            • What jobs are stored in an index (or indices specified via a pattern)?
                                            • @@ -412,8 +412,8 @@ def rollup_search( """ .. raw:: html -

                                              Search rolled-up data. - The rollup search endpoint is needed because, internally, rolled-up documents utilize a different document structure than the original data. +

                                              Search rolled-up data.

                                              +

                                              The rollup search endpoint is needed because, internally, rolled-up documents utilize a different document structure than the original data. It rewrites standard Query DSL into a format that matches the rollup documents then takes the response and rewrites it back to what a client would expect given the original query.

                                              The request body supports a subset of features from the regular search API. The following functionality is not available:

                                              @@ -495,8 +495,8 @@ def start_job( """ .. raw:: html -

                                              Start rollup jobs. - If you try to start a job that does not exist, an exception occurs. +

                                              Start rollup jobs.

                                              +

                                              If you try to start a job that does not exist, an exception occurs. If you try to start a job that is already started, nothing happens.

                                              @@ -543,8 +543,8 @@ def stop_job( """ .. raw:: html -

                                              Stop rollup jobs. - If you try to stop a job that does not exist, an exception occurs. +

                                              Stop rollup jobs.

                                              +

                                              If you try to stop a job that does not exist, an exception occurs. If you try to stop a job that is already stopped, nothing happens.

                                              Since only a stopped job can be deleted, it can be useful to block the API until the indexer has fully stopped. This is accomplished with the wait_for_completion query parameter, and optionally a timeout. For example:

                                              diff --git a/elasticsearch/_sync/client/search_application.py b/elasticsearch/_sync/client/search_application.py index 601498096..10961594d 100644 --- a/elasticsearch/_sync/client/search_application.py +++ b/elasticsearch/_sync/client/search_application.py @@ -90,8 +90,8 @@ def delete_behavioral_analytics( """ .. raw:: html -

                                              Delete a behavioral analytics collection. - The associated data stream is also deleted.

                                              +

                                              Delete a behavioral analytics collection.

                                              +

                                              The associated data stream is also deleted.

                                              ``_ @@ -230,8 +230,8 @@ def list( """ .. raw:: html -

                                              Get search applications. - Get information about search applications.

                                              +

                                              Get search applications.

                                              +

                                              Get information about search applications.

                                              ``_ @@ -460,8 +460,8 @@ def render_query( """ .. raw:: html -

                                              Render a search application query. - Generate an Elasticsearch query using the specified query parameters and the search template associated with the search application or a default template if none is specified. +

                                              Render a search application query.

                                              +

                                              Generate an Elasticsearch query using the specified query parameters and the search template associated with the search application or a default template if none is specified. If a parameter used in the search template is not specified in params, the parameter's default value will be used. The API returns the specific Elasticsearch query that would be generated and run by calling the search application search API.

                                              You must have read privileges on the backing alias of the search application.

                                              @@ -526,8 +526,8 @@ def search( """ .. raw:: html -

                                              Run a search application search. - Generate and run an Elasticsearch query that uses the specified query parameteter and the search template associated with the search application or default template. +

                                              Run a search application search.

                                              +

                                              Generate and run an Elasticsearch query that uses the specified query parameteter and the search template associated with the search application or default template. Unspecified template parameters are assigned their default values if applicable.

                                              diff --git a/elasticsearch/_sync/client/searchable_snapshots.py b/elasticsearch/_sync/client/searchable_snapshots.py index 41231c456..baa48ae6c 100644 --- a/elasticsearch/_sync/client/searchable_snapshots.py +++ b/elasticsearch/_sync/client/searchable_snapshots.py @@ -46,8 +46,8 @@ def cache_stats( """ .. raw:: html -

                                              Get cache statistics. - Get statistics about the shared cache for partially mounted indices.

                                              +

                                              Get cache statistics.

                                              +

                                              Get statistics about the shared cache for partially mounted indices.

                                              ``_ @@ -107,8 +107,8 @@ def clear_cache( """ .. raw:: html -

                                              Clear the cache. - Clear indices and data streams from the shared cache for partially mounted indices.

                                              +

                                              Clear the cache.

                                              +

                                              Clear indices and data streams from the shared cache for partially mounted indices.

                                              ``_ @@ -119,7 +119,7 @@ def clear_cache( into no concrete indices. (This includes `_all` string or when no indices have been specified) :param expand_wildcards: Whether to expand wildcard expression to concrete indices - that are open, closed or both. + that are open, closed or both :param ignore_unavailable: Whether specified concrete indices should be ignored when unavailable (missing or closed) """ @@ -184,8 +184,8 @@ def mount( """ .. raw:: html -

                                              Mount a snapshot. - Mount a snapshot as a searchable snapshot index. +

                                              Mount a snapshot.

                                              +

                                              Mount a snapshot as a searchable snapshot index. Do not use this API for snapshots managed by index lifecycle management (ILM). Manually mounting ILM-managed snapshots can interfere with ILM processes.

                                              diff --git a/elasticsearch/_sync/client/security.py b/elasticsearch/_sync/client/security.py index c605f27e3..6e7436f0c 100644 --- a/elasticsearch/_sync/client/security.py +++ b/elasticsearch/_sync/client/security.py @@ -288,8 +288,8 @@ def bulk_update_api_keys( """ .. raw:: html -

                                              Bulk update API keys. - Update the attributes for multiple API keys.

                                              +

                                              Bulk update API keys.

                                              +

                                              Update the attributes for multiple API keys.

                                              IMPORTANT: It is not possible to use an API key as the authentication credential for this API. To update API keys, the owner user's credentials are required.

                                              This API is similar to the update API key API but enables you to apply the same update to multiple API keys in one API call. This operation can greatly improve performance over making individual updates.

                                              It is not possible to update expired or invalidated API keys.

                                              @@ -892,8 +892,8 @@ def create_service_token( Token names must be unique in the context of the associated service account. They must also be globally unique with their fully qualified names, which are comprised of the service account principal and token name, such as `//`. - :param refresh: If `true` then refresh the affected shards to make this operation - visible to search, if `wait_for` (the default) then wait for a refresh to + :param refresh: If `true` (the default) then refresh the affected shards to make + this operation visible to search, if `wait_for` then wait for a refresh to make this operation visible to search, if `false` then do nothing with refreshes. """ if namespace in SKIP_IN_PATH: @@ -1208,8 +1208,8 @@ def delete_service_token( :param namespace: The namespace, which is a top-level grouping of service accounts. :param service: The service name. :param name: The name of the service account token. - :param refresh: If `true` then refresh the affected shards to make this operation - visible to search, if `wait_for` (the default) then wait for a refresh to + :param refresh: If `true` (the default) then refresh the affected shards to make + this operation visible to search, if `wait_for` then wait for a refresh to make this operation visible to search, if `false` then do nothing with refreshes. """ if namespace in SKIP_IN_PATH: @@ -3750,7 +3750,8 @@ def query_role( :param size: The number of hits to return. It must not be negative. By default, you cannot page through more than 10,000 hits using the `from` and `size` parameters. To page through more hits, use the `search_after` parameter. - :param sort: The sort definition. You can sort on `username`, `roles`, or `enabled`. + :param sort: The sort definition. You can sort on `name`, `description`, `metadata`, + `applications.application`, `applications.privileges`, and `applications.resources`. In addition, sort can also be applied to the `_doc` field to sort by index order. """ diff --git a/elasticsearch/_sync/client/shutdown.py b/elasticsearch/_sync/client/shutdown.py index eb2b17661..80ecef035 100644 --- a/elasticsearch/_sync/client/shutdown.py +++ b/elasticsearch/_sync/client/shutdown.py @@ -48,8 +48,8 @@ def delete_node( """ .. raw:: html -

                                              Cancel node shutdown preparations. - Remove a node from the shutdown list so it can resume normal operations. +

                                              Cancel node shutdown preparations.

                                              +

                                              Remove a node from the shutdown list so it can resume normal operations. You must explicitly clear the shutdown request when a node rejoins the cluster or when a node has permanently left the cluster. Shutdown requests are never removed automatically by Elasticsearch.

                                              NOTE: This feature is designed for indirect use by Elastic Cloud, Elastic Cloud Enterprise, and Elastic Cloud on Kubernetes. @@ -117,7 +117,8 @@ def get_node( ``_ - :param node_id: Which node for which to retrieve the shutdown status + :param node_id: Comma-separated list of nodes for which to retrieve the shutdown + status :param master_timeout: Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. diff --git a/elasticsearch/_sync/client/simulate.py b/elasticsearch/_sync/client/simulate.py index 477b583d2..cac6d88b2 100644 --- a/elasticsearch/_sync/client/simulate.py +++ b/elasticsearch/_sync/client/simulate.py @@ -67,8 +67,8 @@ def ingest( """ .. raw:: html -

                                              Simulate data ingestion. - Run ingest pipelines against a set of provided documents, optionally with substitute pipeline definitions, to simulate ingesting data into an index.

                                              +

                                              Simulate data ingestion.

                                              +

                                              Run ingest pipelines against a set of provided documents, optionally with substitute pipeline definitions, to simulate ingesting data into an index.

                                              This API is meant to be used for troubleshooting or pipeline development, as it does not actually index any data into Elasticsearch.

                                              The API runs the default and final pipeline for that index against a set of documents provided in the body of the request. If a pipeline contains a reroute processor, it follows that reroute processor to the new index, running that index's pipelines as well the same way that a non-simulated ingest would. diff --git a/elasticsearch/_sync/client/slm.py b/elasticsearch/_sync/client/slm.py index 2541d70f6..e4e270b2f 100644 --- a/elasticsearch/_sync/client/slm.py +++ b/elasticsearch/_sync/client/slm.py @@ -40,8 +40,8 @@ def delete_lifecycle( """ .. raw:: html -

                                              Delete a policy. - Delete a snapshot lifecycle policy definition. +

                                              Delete a policy.

                                              +

                                              Delete a snapshot lifecycle policy definition. This operation prevents any future snapshots from being taken but does not cancel in-progress snapshots or remove previously-taken snapshots.

                                              @@ -96,8 +96,8 @@ def execute_lifecycle( """ .. raw:: html -

                                              Run a policy. - Immediately create a snapshot according to the snapshot lifecycle policy without waiting for the scheduled time. +

                                              Run a policy.

                                              +

                                              Immediately create a snapshot according to the snapshot lifecycle policy without waiting for the scheduled time. The snapshot policy is normally applied according to its schedule, but you might want to manually run a policy before performing an upgrade or other maintenance.

                                              @@ -151,8 +151,8 @@ def execute_retention( """ .. raw:: html -

                                              Run a retention policy. - Manually apply the retention policy to force immediate removal of snapshots that are expired according to the snapshot lifecycle policy retention rules. +

                                              Run a retention policy.

                                              +

                                              Manually apply the retention policy to force immediate removal of snapshots that are expired according to the snapshot lifecycle policy retention rules. The retention policy is normally applied according to its schedule.

                                              @@ -204,13 +204,13 @@ def get_lifecycle( """ .. raw:: html -

                                              Get policy information. - Get snapshot lifecycle policy definitions and information about the latest snapshot attempts.

                                              +

                                              Get policy information.

                                              +

                                              Get snapshot lifecycle policy definitions and information about the latest snapshot attempts.

                                              ``_ - :param policy_id: Comma-separated list of snapshot lifecycle policies to retrieve + :param policy_id: A comma-separated list of snapshot lifecycle policy identifiers. :param master_timeout: The period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. @@ -261,8 +261,8 @@ def get_stats( """ .. raw:: html -

                                              Get snapshot lifecycle management statistics. - Get global and policy-level statistics about actions taken by snapshot lifecycle management.

                                              +

                                              Get snapshot lifecycle management statistics.

                                              +

                                              Get global and policy-level statistics about actions taken by snapshot lifecycle management.

                                              ``_ @@ -373,8 +373,8 @@ def put_lifecycle( """ .. raw:: html -

                                              Create or update a policy. - Create or update a snapshot lifecycle policy. +

                                              Create or update a policy.

                                              +

                                              Create or update a snapshot lifecycle policy. If the policy already exists, this request increments the policy version. Only the latest version of a policy is stored.

                                              @@ -456,8 +456,8 @@ def start( """ .. raw:: html -

                                              Start snapshot lifecycle management. - Snapshot lifecycle management (SLM) starts automatically when a cluster is formed. +

                                              Start snapshot lifecycle management.

                                              +

                                              Snapshot lifecycle management (SLM) starts automatically when a cluster is formed. Manually starting SLM is necessary only if it has been stopped using the stop SLM API.

                                              @@ -510,8 +510,8 @@ def stop( """ .. raw:: html -

                                              Stop snapshot lifecycle management. - Stop all snapshot lifecycle management (SLM) operations and the SLM plugin. +

                                              Stop snapshot lifecycle management.

                                              +

                                              Stop all snapshot lifecycle management (SLM) operations and the SLM plugin. This API is useful when you are performing maintenance on a cluster and need to prevent SLM from performing any actions on your data streams or indices. Stopping SLM does not stop any snapshots that are in progress. You can manually trigger snapshots with the run snapshot lifecycle policy API even if SLM is stopped.

                                              diff --git a/elasticsearch/_sync/client/snapshot.py b/elasticsearch/_sync/client/snapshot.py index 30888d2ca..53bebb42d 100644 --- a/elasticsearch/_sync/client/snapshot.py +++ b/elasticsearch/_sync/client/snapshot.py @@ -46,8 +46,8 @@ def cleanup_repository( """ .. raw:: html -

                                              Clean up the snapshot repository. - Trigger the review of the contents of a snapshot repository and delete any stale data not referenced by existing snapshots.

                                              +

                                              Clean up the snapshot repository.

                                              +

                                              Trigger the review of the contents of a snapshot repository and delete any stale data not referenced by existing snapshots.

                                              ``_ @@ -110,8 +110,8 @@ def clone( """ .. raw:: html -

                                              Clone a snapshot. - Clone part of all of a snapshot into another snapshot in the same repository.

                                              +

                                              Clone a snapshot.

                                              +

                                              Clone part of all of a snapshot into another snapshot in the same repository.

                                              ``_ @@ -207,8 +207,8 @@ def create( """ .. raw:: html -

                                              Create a snapshot. - Take a snapshot of a cluster or of data streams and indices.

                                              +

                                              Create a snapshot.

                                              +

                                              Take a snapshot of a cluster or of data streams and indices.

                                              ``_ @@ -330,8 +330,8 @@ def create_repository( """ .. raw:: html -

                                              Create or update a snapshot repository. - IMPORTANT: If you are migrating searchable snapshots, the repository name must be identical in the source and destination clusters. +

                                              Create or update a snapshot repository.

                                              +

                                              IMPORTANT: If you are migrating searchable snapshots, the repository name must be identical in the source and destination clusters. To register a snapshot repository, the cluster's global metadata must be writeable. Ensure there are no cluster blocks (for example, cluster.blocks.read_only and clsuter.blocks.read_only_allow_delete settings) that prevent write access.

                                              Several options for this API can be specified using a query parameter or a request body parameter. @@ -470,8 +470,8 @@ def delete_repository( """ .. raw:: html -

                                              Delete snapshot repositories. - When a repository is unregistered, Elasticsearch removes only the reference to the location where the repository is storing the snapshots. +

                                              Delete snapshot repositories.

                                              +

                                              When a repository is unregistered, Elasticsearch removes only the reference to the location where the repository is storing the snapshots. The snapshots themselves are left untouched and in place.

                                              @@ -976,8 +976,8 @@ def repository_verify_integrity( """ .. raw:: html -

                                              Verify the repository integrity. - Verify the integrity of the contents of a snapshot repository.

                                              +

                                              Verify the repository integrity.

                                              +

                                              Verify the integrity of the contents of a snapshot repository.

                                              This API enables you to perform a comprehensive check of the contents of a repository, looking for any anomalies in its data or metadata which might prevent you from restoring snapshots from the repository or which might cause future snapshot create or delete operations to fail.

                                              If you suspect the integrity of the contents of one of your snapshot repositories, cease all write activity to this repository immediately, set its read_only option to true, and use this API to verify its integrity. Until you do so:

                                              @@ -1115,8 +1115,8 @@ def restore( """ .. raw:: html -

                                              Restore a snapshot. - Restore a snapshot of a cluster or data streams and indices.

                                              +

                                              Restore a snapshot.

                                              +

                                              Restore a snapshot of a cluster or data streams and indices.

                                              You can restore a snapshot only to a running cluster with an elected master node. The snapshot repository must be registered and available to the cluster. The snapshot and cluster versions must be compatible.

                                              @@ -1264,8 +1264,8 @@ def status( """ .. raw:: html -

                                              Get the snapshot status. - Get a detailed description of the current state for each shard participating in the snapshot.

                                              +

                                              Get the snapshot status.

                                              +

                                              Get a detailed description of the current state for each shard participating in the snapshot.

                                              Note that this API should be used only to obtain detailed shard-level information for ongoing snapshots. If this detail is not needed or you want to obtain information about one or more existing snapshots, use the get snapshot API.

                                              If you omit the <snapshot> request path parameter, the request retrieves information only for currently running snapshots. @@ -1347,8 +1347,8 @@ def verify_repository( """ .. raw:: html -

                                              Verify a snapshot repository. - Check for common misconfigurations in a snapshot repository.

                                              +

                                              Verify a snapshot repository.

                                              +

                                              Check for common misconfigurations in a snapshot repository.

                                              ``_ diff --git a/elasticsearch/_sync/client/sql.py b/elasticsearch/_sync/client/sql.py index bee0ba437..f34283e99 100644 --- a/elasticsearch/_sync/client/sql.py +++ b/elasticsearch/_sync/client/sql.py @@ -89,8 +89,8 @@ def delete_async( """ .. raw:: html -

                                              Delete an async SQL search. - Delete an async SQL search or a stored synchronous SQL search. +

                                              Delete an async SQL search.

                                              +

                                              Delete an async SQL search or a stored synchronous SQL search. If the search is still running, the API cancels it.

                                              If the Elasticsearch security features are enabled, only the following users can use this API to delete a search:

                                                @@ -145,8 +145,8 @@ def get_async( """ .. raw:: html -

                                                Get async SQL search results. - Get the current status and available results for an async SQL search or stored synchronous SQL search.

                                                +

                                                Get async SQL search results.

                                                +

                                                Get the current status and available results for an async SQL search or stored synchronous SQL search.

                                                If the Elasticsearch security features are enabled, only the user who first submitted the SQL search can retrieve the search using this API.

                                                @@ -208,8 +208,8 @@ def get_async_status( """ .. raw:: html -

                                                Get the async SQL search status. - Get the current status of an async SQL search or a stored synchronous SQL search.

                                                +

                                                Get the async SQL search status.

                                                +

                                                Get the current status of an async SQL search or a stored synchronous SQL search.

                                                ``_ @@ -298,8 +298,8 @@ def query( """ .. raw:: html -

                                                Get SQL search results. - Run an SQL request.

                                                +

                                                Get SQL search results.

                                                +

                                                Run an SQL request.

                                                ``_ @@ -429,8 +429,8 @@ def translate( """ .. raw:: html -

                                                Translate SQL into Elasticsearch queries. - Translate an SQL search into a search API request containing Query DSL. +

                                                Translate SQL into Elasticsearch queries.

                                                +

                                                Translate an SQL search into a search API request containing Query DSL. It accepts the same request body parameters as the SQL search API, excluding cursor.

                                                diff --git a/elasticsearch/_sync/client/streams.py b/elasticsearch/_sync/client/streams.py index ed208cde8..b2c60e979 100644 --- a/elasticsearch/_sync/client/streams.py +++ b/elasticsearch/_sync/client/streams.py @@ -72,7 +72,7 @@ def logs_disable( __query["pretty"] = pretty if timeout is not None: __query["timeout"] = timeout - __headers = {"accept": "application/json,text/plain"} + __headers = {"accept": "text/plain,application/json"} return self.perform_request( # type: ignore[return-value] "POST", __path, @@ -127,7 +127,7 @@ def logs_enable( __query["pretty"] = pretty if timeout is not None: __query["timeout"] = timeout - __headers = {"accept": "application/json,text/plain"} + __headers = {"accept": "text/plain,application/json"} return self.perform_request( # type: ignore[return-value] "POST", __path, diff --git a/elasticsearch/_sync/client/synonyms.py b/elasticsearch/_sync/client/synonyms.py index 8731f40fd..b0199685f 100644 --- a/elasticsearch/_sync/client/synonyms.py +++ b/elasticsearch/_sync/client/synonyms.py @@ -95,8 +95,8 @@ def delete_synonym_rule( """ .. raw:: html -

                                                Delete a synonym rule. - Delete a synonym rule from a synonym set.

                                                +

                                                Delete a synonym rule.

                                                +

                                                Delete a synonym rule from a synonym set.

                                                ``_ @@ -204,8 +204,8 @@ def get_synonym_rule( """ .. raw:: html -

                                                Get a synonym rule. - Get a synonym rule from a synonym set.

                                                +

                                                Get a synonym rule.

                                                +

                                                Get a synonym rule from a synonym set.

                                                ``_ @@ -257,8 +257,8 @@ def get_synonyms_sets( """ .. raw:: html -

                                                Get all synonym sets. - Get a summary of all defined synonym sets.

                                                +

                                                Get all synonym sets.

                                                +

                                                Get a summary of all defined synonym sets.

                                                ``_ @@ -311,8 +311,8 @@ def put_synonym( """ .. raw:: html -

                                                Create or update a synonym set. - Synonyms sets are limited to a maximum of 10,000 synonym rules per set. +

                                                Create or update a synonym set.

                                                +

                                                Synonyms sets are limited to a maximum of 10,000 synonym rules per set. If you need to manage more synonym rules, you can create multiple synonym sets.

                                                When an existing synonyms set is updated, the search analyzers that use the synonyms set are reloaded automatically for all indices. This is equivalent to invoking the reload search analyzers API for all indices that use the synonyms set.

                                                @@ -378,8 +378,8 @@ def put_synonym_rule( """ .. raw:: html -

                                                Create or update a synonym rule. - Create or update a synonym rule in a synonym set.

                                                +

                                                Create or update a synonym rule.

                                                +

                                                Create or update a synonym rule in a synonym set.

                                                If any of the synonym rules included is invalid, the API returns an error.

                                                When you update a synonym rule, all analyzers using the synonyms set will be reloaded automatically to reflect the new rule.

                                                diff --git a/elasticsearch/_sync/client/tasks.py b/elasticsearch/_sync/client/tasks.py index 811ac1731..f3f0ba034 100644 --- a/elasticsearch/_sync/client/tasks.py +++ b/elasticsearch/_sync/client/tasks.py @@ -121,8 +121,8 @@ def get( """ .. raw:: html -

                                                Get task information. - Get information about a task currently running in the cluster.

                                                +

                                                Get task information.

                                                +

                                                Get information about a task currently running in the cluster.

                                                WARNING: The task management API is new and should still be considered a beta feature. The API may change in ways that are not backwards compatible.

                                                If the task identifier is not found, a 404 response code indicates that there are no resources that match the request.

                                                @@ -185,8 +185,8 @@ def list( """ .. raw:: html -

                                                Get all tasks. - Get information about the tasks currently running on one or more nodes in the cluster.

                                                +

                                                Get all tasks.

                                                +

                                                Get information about the tasks currently running on one or more nodes in the cluster.

                                                WARNING: The task management API is new and should still be considered a beta feature. The API may change in ways that are not backwards compatible.

                                                Identifying running tasks

                                                diff --git a/elasticsearch/_sync/client/text_structure.py b/elasticsearch/_sync/client/text_structure.py index fa3218f81..5a64961c7 100644 --- a/elasticsearch/_sync/client/text_structure.py +++ b/elasticsearch/_sync/client/text_structure.py @@ -55,8 +55,8 @@ def find_field_structure( """ .. raw:: html -

                                                Find the structure of a text field. - Find the structure of a text field in an Elasticsearch index.

                                                +

                                                Find the structure of a text field.

                                                +

                                                Find the structure of a text field in an Elasticsearch index.

                                                This API provides a starting point for extracting further information from log messages already ingested into Elasticsearch. For example, if you have ingested data into a very simple index that has just @timestamp and message fields, you can use this API to see what common structure exists in the message field.

                                                The response from the API contains:

                                                @@ -241,8 +241,8 @@ def find_message_structure( """ .. raw:: html -

                                                Find the structure of text messages. - Find the structure of a list of text messages. +

                                                Find the structure of text messages.

                                                +

                                                Find the structure of a list of text messages. The messages must contain data that is suitable to be ingested into Elasticsearch.

                                                This API provides a starting point for ingesting data into Elasticsearch in a format that is suitable for subsequent use with other Elastic Stack functionality. Use this API rather than the find text structure API if your input text has already been split up into separate messages by some other process.

                                                @@ -402,7 +402,11 @@ def find_structure( delimiter: t.Optional[str] = None, ecs_compatibility: t.Optional[str] = None, explain: t.Optional[bool] = None, - format: t.Optional[str] = None, + format: t.Optional[ + t.Union[ + str, t.Literal["delimited", "ndjson", "semi_structured_text", "xml"] + ] + ] = None, grok_pattern: t.Optional[str] = None, has_header_row: t.Optional[bool] = None, line_merge_size_limit: t.Optional[int] = None, @@ -416,8 +420,8 @@ def find_structure( """ .. raw:: html -

                                                Find the structure of a text file. - The text file must contain data that is suitable to be ingested into Elasticsearch.

                                                +

                                                Find the structure of a text file.

                                                +

                                                The text file must contain data that is suitable to be ingested into Elasticsearch.

                                                This API provides a starting point for ingesting data into Elasticsearch in a format that is suitable for subsequent use with other Elastic Stack functionality. Unlike other Elasticsearch endpoints, the data that is posted to this endpoint does not need to be UTF-8 encoded and in JSON format. It must, however, be text; binary text formats are not currently supported. @@ -615,8 +619,8 @@ def test_grok_pattern( """ .. raw:: html -

                                                Test a Grok pattern. - Test a Grok pattern on one or more lines of text. +

                                                Test a Grok pattern.

                                                +

                                                Test a Grok pattern on one or more lines of text. The API indicates whether the lines match the pattern together with the offsets and lengths of the matched substrings.

                                                diff --git a/elasticsearch/_sync/client/transform.py b/elasticsearch/_sync/client/transform.py index c8c8b0d8b..2ed2652da 100644 --- a/elasticsearch/_sync/client/transform.py +++ b/elasticsearch/_sync/client/transform.py @@ -85,6 +85,45 @@ def delete_transform( path_parts=__path_parts, ) + @_rewrite_parameters() + def get_node_stats( + self, + *, + error_trace: t.Optional[bool] = None, + filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, + human: t.Optional[bool] = None, + pretty: t.Optional[bool] = None, + ) -> ObjectApiResponse[t.Any]: + """ + .. raw:: html + +

                                                Get node stats.

                                                +

                                                Get per-node information about transform usage.

                                                + + + ``_ + """ + __path_parts: t.Dict[str, str] = {} + __path = "/_transform/_node_stats" + __query: t.Dict[str, t.Any] = {} + if error_trace is not None: + __query["error_trace"] = error_trace + if filter_path is not None: + __query["filter_path"] = filter_path + if human is not None: + __query["human"] = human + if pretty is not None: + __query["pretty"] = pretty + __headers = {"accept": "application/json"} + return self.perform_request( # type: ignore[return-value] + "GET", + __path, + params=__query, + headers=__headers, + endpoint_id="transform.get_node_stats", + path_parts=__path_parts, + ) + @_rewrite_parameters( parameter_aliases={"from": "from_"}, ) @@ -104,8 +143,8 @@ def get_transform( """ .. raw:: html -

                                                Get transforms. - Get configuration information for transforms.

                                                +

                                                Get transforms.

                                                +

                                                Get configuration information for transforms.

                                                ``_ @@ -262,8 +301,8 @@ def preview_transform( """ .. raw:: html -

                                                Preview a transform. - Generates a preview of the results that you will get when you create a transform with the same configuration.

                                                +

                                                Preview a transform.

                                                +

                                                Generates a preview of the results that you will get when you create a transform with the same configuration.

                                                It returns a maximum of 100 results. The calculations are based on all the current data in the source index. It also generates a list of mappings and settings for the destination index. These values are determined based on the field types of the source index and the transform aggregations.

                                                @@ -386,8 +425,8 @@ def put_transform( """ .. raw:: html -

                                                Create a transform. - Creates a transform.

                                                +

                                                Create a transform.

                                                +

                                                Creates a transform.

                                                A transform copies data from source indices, transforms it, and persists it into an entity-centric destination index. You can also think of the destination index as a two-dimensional tabular data structure (known as a data frame). The ID for each document in the data frame is generated from a hash of the entity, so there is a unique row per entity.

                                                @@ -616,8 +655,8 @@ def set_upgrade_mode( """ .. raw:: html -

                                                Set upgrade_mode for transform indices. - Sets a cluster wide upgrade_mode setting that prepares transform +

                                                Set upgrade_mode for transform indices.

                                                +

                                                Sets a cluster wide upgrade_mode setting that prepares transform indices for an upgrade. When upgrading your cluster, in some circumstances you must restart your nodes and reindex your transform indices. In those circumstances, @@ -749,8 +788,8 @@ def stop_transform( """ .. raw:: html -

                                                Stop transforms. - Stops one or more transforms.

                                                +

                                                Stop transforms.

                                                +

                                                Stops one or more transforms.

                                                ``_ @@ -846,8 +885,8 @@ def update_transform( """ .. raw:: html -

                                                Update a transform. - Updates certain properties of a transform.

                                                +

                                                Update a transform.

                                                +

                                                Updates certain properties of a transform.

                                                All updated properties except description do not take effect until after the transform starts the next checkpoint, thus there is data consistency in each checkpoint. To use this API, you must have read and view_index_metadata privileges for the source indices. You must also have index and read privileges for the destination index. When diff --git a/elasticsearch/_sync/client/watcher.py b/elasticsearch/_sync/client/watcher.py index d14f8481d..73f5eecb2 100644 --- a/elasticsearch/_sync/client/watcher.py +++ b/elasticsearch/_sync/client/watcher.py @@ -39,8 +39,8 @@ def ack_watch( """ .. raw:: html -

                                                Acknowledge a watch. - Acknowledging a watch enables you to manually throttle the execution of the watch's actions.

                                                +

                                                Acknowledge a watch.

                                                +

                                                Acknowledging a watch enables you to manually throttle the execution of the watch's actions.

                                                The acknowledgement state of an action is stored in the status.actions.<id>.ack.state structure.

                                                IMPORTANT: If the specified watch is currently being executed, this API will return an error The reason for this behavior is to prevent overwriting the watch status from a watch execution.

                                                @@ -101,8 +101,8 @@ def activate_watch( """ .. raw:: html -

                                                Activate a watch. - A watch can be either active or inactive.

                                                +

                                                Activate a watch.

                                                +

                                                A watch can be either active or inactive.

                                                ``_ @@ -145,8 +145,8 @@ def deactivate_watch( """ .. raw:: html -

                                                Deactivate a watch. - A watch can be either active or inactive.

                                                +

                                                Deactivate a watch.

                                                +

                                                A watch can be either active or inactive.

                                                ``_ @@ -189,8 +189,8 @@ def delete_watch( """ .. raw:: html -

                                                Delete a watch. - When the watch is removed, the document representing the watch in the .watches index is gone and it will never be run again.

                                                +

                                                Delete a watch.

                                                +

                                                When the watch is removed, the document representing the watch in the .watches index is gone and it will never be run again.

                                                Deleting a watch does not delete any watch execution records related to this watch from the watch history.

                                                IMPORTANT: Deleting a watch must be done by using only this API. Do not delete the watch directly from the .watches index using the Elasticsearch delete document API @@ -266,8 +266,8 @@ def execute_watch( """ .. raw:: html -

                                                Run a watch. - This API can be used to force execution of the watch outside of its triggering logic or to simulate the watch execution for debugging purposes.

                                                +

                                                Run a watch.

                                                +

                                                This API can be used to force execution of the watch outside of its triggering logic or to simulate the watch execution for debugging purposes.

                                                For testing and debugging purposes, you also have fine-grained control on how the watch runs. You can run the watch without running all of its actions or alternatively by simulating them. You can also force execution by ignoring the watch condition and control whether a watch record would be written to the watch history after it runs.

                                                @@ -362,8 +362,8 @@ def get_settings( """ .. raw:: html -

                                                Get Watcher index settings. - Get settings for the Watcher internal index (.watches). +

                                                Get Watcher index settings.

                                                +

                                                Get settings for the Watcher internal index (.watches). Only a subset of settings are shown, for example index.auto_expand_replicas and index.number_of_replicas.

                                                @@ -476,8 +476,8 @@ def put_watch( """ .. raw:: html -

                                                Create or update a watch. - When a watch is registered, a new document that represents the watch is added to the .watches index and its trigger is immediately registered with the relevant trigger engine. +

                                                Create or update a watch.

                                                +

                                                When a watch is registered, a new document that represents the watch is added to the .watches index and its trigger is immediately registered with the relevant trigger engine. Typically for the schedule trigger, the scheduler is the trigger engine.

                                                IMPORTANT: You must use Kibana or this API to create a watch. Do not add a watch directly to the .watches index by using the Elasticsearch index API. @@ -494,9 +494,9 @@ def put_watch( :param active: The initial state of the watch. The default value is `true`, which means the watch is active by default. :param condition: The condition that defines if the actions should be run. - :param if_primary_term: only update the watch if the last operation that has + :param if_primary_term: Only update the watch if the last operation that has changed the watch has the specified primary term - :param if_seq_no: only update the watch if the last operation that has changed + :param if_seq_no: Only update the watch if the last operation that has changed the watch has the specified sequence number :param input: The input that defines the input that loads the data for the watch. :param metadata: Metadata JSON that will be copied into the history entries. @@ -591,8 +591,8 @@ def query_watches( """ .. raw:: html -

                                                Query watches. - Get all registered watches in a paginated manner and optionally filter watches by a query.

                                                +

                                                Query watches.

                                                +

                                                Get all registered watches in a paginated manner and optionally filter watches by a query.

                                                Note that only the _id and metadata.* fields are queryable or sortable.

                                                @@ -667,8 +667,8 @@ def start( """ .. raw:: html -

                                                Start the watch service. - Start the Watcher service if it is not already running.

                                                +

                                                Start the watch service.

                                                +

                                                Start the Watcher service if it is not already running.

                                                ``_ @@ -732,8 +732,8 @@ def stats( """ .. raw:: html -

                                                Get Watcher statistics. - This API always returns basic metrics. +

                                                Get Watcher statistics.

                                                +

                                                This API always returns basic metrics. You retrieve more metrics by using the metric parameter.

                                                @@ -784,8 +784,8 @@ def stop( """ .. raw:: html -

                                                Stop the watch service. - Stop the Watcher service if it is running.

                                                +

                                                Stop the watch service.

                                                +

                                                Stop the Watcher service if it is running.

                                                ``_ @@ -840,8 +840,8 @@ def update_settings( """ .. raw:: html -

                                                Update Watcher index settings. - Update settings for the Watcher internal index (.watches). +

                                                Update Watcher index settings.

                                                +

                                                Update settings for the Watcher internal index (.watches). Only a subset of settings can be modified. This includes index.auto_expand_replicas, index.number_of_replicas, index.routing.allocation.exclude.*, index.routing.allocation.include.* and index.routing.allocation.require.*. diff --git a/elasticsearch/_sync/client/xpack.py b/elasticsearch/_sync/client/xpack.py index b44cd0909..7361c5a18 100644 --- a/elasticsearch/_sync/client/xpack.py +++ b/elasticsearch/_sync/client/xpack.py @@ -45,8 +45,8 @@ def info( """ .. raw:: html -

                                                Get information. - The information provided by the API includes:

                                                +

                                                Get information.

                                                +

                                                The information provided by the API includes:

                                                • Build information including the build number and timestamp.
                                                • License information about the currently installed license.
                                                • @@ -56,7 +56,8 @@ def info( ``_ - :param accept_enterprise: If this param is used it must be set to true + :param accept_enterprise: If used, this otherwise ignored parameter must be set + to true :param categories: A comma-separated list of the information categories to include in the response. For example, `build,license,features`. """ @@ -98,8 +99,8 @@ def usage( """ .. raw:: html -

                                                  Get usage information. - Get information about the features that are currently enabled and available under the current license. +

                                                  Get usage information.

                                                  +

                                                  Get information about the features that are currently enabled and available under the current license. The API also provides some usage statistics.

                                                  diff --git a/elasticsearch/_version.py b/elasticsearch/_version.py index d55ac926d..66f650568 100644 --- a/elasticsearch/_version.py +++ b/elasticsearch/_version.py @@ -16,4 +16,4 @@ # under the License. __versionstr__ = "9.2.0" -__es_specification_commit__ = "2f74c26e0a1d66c42232ce2830652c01e8717f00" +__es_specification_commit__ = "434fcc43a17b5bad65d940358ae114d8891e351b" diff --git a/elasticsearch/dsl/aggs.py b/elasticsearch/dsl/aggs.py index 2a6b2ff91..439955c98 100644 --- a/elasticsearch/dsl/aggs.py +++ b/elasticsearch/dsl/aggs.py @@ -1495,7 +1495,7 @@ def __init__( "DefaultType", ] = DEFAULT, field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, - precision: Union[float, str, "DefaultType"] = DEFAULT, + precision: Union[int, str, "DefaultType"] = DEFAULT, shard_size: Union[int, "DefaultType"] = DEFAULT, size: Union[int, "DefaultType"] = DEFAULT, **kwargs: Any, @@ -1579,7 +1579,7 @@ def __init__( self, *, field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, - precision: Union[float, "DefaultType"] = DEFAULT, + precision: Union[int, "DefaultType"] = DEFAULT, shard_size: Union[int, "DefaultType"] = DEFAULT, size: Union[int, "DefaultType"] = DEFAULT, bounds: Union[ @@ -2680,7 +2680,7 @@ def __init__( self, *, keyed: Union[bool, "DefaultType"] = DEFAULT, - percents: Union[Sequence[float], "DefaultType"] = DEFAULT, + percents: Union[float, Sequence[float], "DefaultType"] = DEFAULT, hdr: Union["types.HdrMethod", Dict[str, Any], "DefaultType"] = DEFAULT, tdigest: Union["types.TDigest", Dict[str, Any], "DefaultType"] = DEFAULT, format: Union[str, "DefaultType"] = DEFAULT, diff --git a/elasticsearch/dsl/field.py b/elasticsearch/dsl/field.py index 3b5075287..63bf1e9b2 100644 --- a/elasticsearch/dsl/field.py +++ b/elasticsearch/dsl/field.py @@ -3892,7 +3892,7 @@ def __init__( "types.SemanticTextIndexOptions", Dict[str, Any], "DefaultType" ] = DEFAULT, chunking_settings: Union[ - "types.ChunkingSettings", None, Dict[str, Any], "DefaultType" + "types.ChunkingSettings", Dict[str, Any], "DefaultType" ] = DEFAULT, fields: Union[Mapping[str, Field], "DefaultType"] = DEFAULT, **kwargs: Any, diff --git a/elasticsearch/dsl/types.py b/elasticsearch/dsl/types.py index b62fad025..f2ecca427 100644 --- a/elasticsearch/dsl/types.py +++ b/elasticsearch/dsl/types.py @@ -400,8 +400,6 @@ class DenseVectorIndexOptions(AttrDict[Any]): :arg rescore_vector: The rescore vector options. This is only applicable to `bbq_disk`, `bbq_hnsw`, `int4_hnsw`, `int8_hnsw`, `bbq_flat`, `int4_flat`, and `int8_flat` index types. - :arg on_disk_rescore: `true` if vector rescoring should be done on- - disk Only applicable to `bbq_hnsw` """ type: Union[ @@ -424,7 +422,6 @@ class DenseVectorIndexOptions(AttrDict[Any]): rescore_vector: Union[ "DenseVectorIndexOptionsRescoreVector", Dict[str, Any], DefaultType ] - on_disk_rescore: Union[bool, DefaultType] def __init__( self, @@ -449,7 +446,6 @@ def __init__( rescore_vector: Union[ "DenseVectorIndexOptionsRescoreVector", Dict[str, Any], DefaultType ] = DEFAULT, - on_disk_rescore: Union[bool, DefaultType] = DEFAULT, **kwargs: Any, ): if type is not DEFAULT: @@ -462,8 +458,6 @@ def __init__( kwargs["m"] = m if rescore_vector is not DEFAULT: kwargs["rescore_vector"] = rescore_vector - if on_disk_rescore is not DEFAULT: - kwargs["on_disk_rescore"] = on_disk_rescore super().__init__(kwargs) From b0f28b1d55cd2ee417b769126d9aed96484c259a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 10:42:34 +0000 Subject: [PATCH 34/36] Use relative imports to fix elasticsearch9 package imports (#3232) (#3234) Fixes #3224 (cherry picked from commit 6bc97883e050d681bb9ba11bc7307a38e84c324f) Co-authored-by: Miguel Grinberg --- elasticsearch/_async/client/esql.py | 2 +- elasticsearch/_sync/client/esql.py | 2 +- elasticsearch/dsl/_async/document.py | 9 ++++----- elasticsearch/dsl/_async/index.py | 2 +- elasticsearch/dsl/_async/search.py | 5 ++--- elasticsearch/dsl/_sync/document.py | 9 ++++----- elasticsearch/dsl/_sync/index.py | 2 +- elasticsearch/dsl/_sync/search.py | 5 ++--- elasticsearch/dsl/async_connections.py | 3 +-- elasticsearch/dsl/connections.py | 3 +-- elasticsearch/dsl/pydantic.py | 2 +- elasticsearch/dsl/serializer.py | 3 +-- elasticsearch/dsl/utils.py | 3 +-- elasticsearch/esql/functions.py | 4 ++-- elasticsearch/helpers/vectorstore/__init__.py | 14 +++++++------- elasticsearch/helpers/vectorstore/_async/_utils.py | 2 +- .../vectorstore/_async/embedding_service.py | 4 ++-- .../helpers/vectorstore/_async/strategies.py | 6 +++--- .../helpers/vectorstore/_async/vectorstore.py | 10 +++++----- elasticsearch/helpers/vectorstore/_sync/_utils.py | 2 +- .../helpers/vectorstore/_sync/embedding_service.py | 4 ++-- .../helpers/vectorstore/_sync/strategies.py | 6 +++--- .../helpers/vectorstore/_sync/vectorstore.py | 10 +++++----- 23 files changed, 52 insertions(+), 60 deletions(-) diff --git a/elasticsearch/_async/client/esql.py b/elasticsearch/_async/client/esql.py index 11325a129..68c97e08a 100644 --- a/elasticsearch/_async/client/esql.py +++ b/elasticsearch/_async/client/esql.py @@ -29,7 +29,7 @@ ) if t.TYPE_CHECKING: - from elasticsearch.esql import ESQLBase + from ...esql import ESQLBase class EsqlClient(NamespacedClient): diff --git a/elasticsearch/_sync/client/esql.py b/elasticsearch/_sync/client/esql.py index 05d0f3e43..17a05481f 100644 --- a/elasticsearch/_sync/client/esql.py +++ b/elasticsearch/_sync/client/esql.py @@ -29,7 +29,7 @@ ) if t.TYPE_CHECKING: - from elasticsearch.esql import ESQLBase + from ...esql import ESQLBase class EsqlClient(NamespacedClient): diff --git a/elasticsearch/dsl/_async/document.py b/elasticsearch/dsl/_async/document.py index c3631260a..9815fb20e 100644 --- a/elasticsearch/dsl/_async/document.py +++ b/elasticsearch/dsl/_async/document.py @@ -31,9 +31,8 @@ from typing_extensions import Self, dataclass_transform -from elasticsearch.exceptions import NotFoundError, RequestError -from elasticsearch.helpers import async_bulk - +from ...exceptions import NotFoundError, RequestError +from ...helpers import async_bulk from .._async.index import AsyncIndex from ..async_connections import get_connection from ..document_base import DocumentBase, DocumentMeta, mapped_field @@ -42,8 +41,8 @@ from .search import AsyncSearch if TYPE_CHECKING: - from elasticsearch import AsyncElasticsearch - from elasticsearch.esql.esql import ESQLBase + from ... import AsyncElasticsearch + from ...esql.esql import ESQLBase class AsyncIndexMeta(DocumentMeta): diff --git a/elasticsearch/dsl/_async/index.py b/elasticsearch/dsl/_async/index.py index 58369579b..0795d47d2 100644 --- a/elasticsearch/dsl/_async/index.py +++ b/elasticsearch/dsl/_async/index.py @@ -30,7 +30,7 @@ if TYPE_CHECKING: from elastic_transport import ObjectApiResponse - from elasticsearch import AsyncElasticsearch + from ... import AsyncElasticsearch class AsyncIndexTemplate: diff --git a/elasticsearch/dsl/_async/search.py b/elasticsearch/dsl/_async/search.py index 2ea277a07..b4911fd58 100644 --- a/elasticsearch/dsl/_async/search.py +++ b/elasticsearch/dsl/_async/search.py @@ -29,9 +29,8 @@ from typing_extensions import Self -from elasticsearch.exceptions import ApiError -from elasticsearch.helpers import async_scan - +from ...exceptions import ApiError +from ...helpers import async_scan from ..async_connections import get_connection from ..response import Response from ..search_base import MultiSearchBase, SearchBase diff --git a/elasticsearch/dsl/_sync/document.py b/elasticsearch/dsl/_sync/document.py index c37c7639d..8b62531c9 100644 --- a/elasticsearch/dsl/_sync/document.py +++ b/elasticsearch/dsl/_sync/document.py @@ -31,9 +31,8 @@ from typing_extensions import Self, dataclass_transform -from elasticsearch.exceptions import NotFoundError, RequestError -from elasticsearch.helpers import bulk - +from ...exceptions import NotFoundError, RequestError +from ...helpers import bulk from .._sync.index import Index from ..connections import get_connection from ..document_base import DocumentBase, DocumentMeta, mapped_field @@ -42,8 +41,8 @@ from .search import Search if TYPE_CHECKING: - from elasticsearch import Elasticsearch - from elasticsearch.esql.esql import ESQLBase + from ... import Elasticsearch + from ...esql.esql import ESQLBase class IndexMeta(DocumentMeta): diff --git a/elasticsearch/dsl/_sync/index.py b/elasticsearch/dsl/_sync/index.py index b2d5830d9..9f9f1e0d1 100644 --- a/elasticsearch/dsl/_sync/index.py +++ b/elasticsearch/dsl/_sync/index.py @@ -30,7 +30,7 @@ if TYPE_CHECKING: from elastic_transport import ObjectApiResponse - from elasticsearch import Elasticsearch + from ... import Elasticsearch class IndexTemplate: diff --git a/elasticsearch/dsl/_sync/search.py b/elasticsearch/dsl/_sync/search.py index 4dfbdb92b..7538eb4f1 100644 --- a/elasticsearch/dsl/_sync/search.py +++ b/elasticsearch/dsl/_sync/search.py @@ -28,9 +28,8 @@ from typing_extensions import Self -from elasticsearch.exceptions import ApiError -from elasticsearch.helpers import scan - +from ...exceptions import ApiError +from ...helpers import scan from ..connections import get_connection from ..response import Response from ..search_base import MultiSearchBase, SearchBase diff --git a/elasticsearch/dsl/async_connections.py b/elasticsearch/dsl/async_connections.py index 8a23d3828..0b51e3226 100644 --- a/elasticsearch/dsl/async_connections.py +++ b/elasticsearch/dsl/async_connections.py @@ -17,8 +17,7 @@ from typing import Type -from elasticsearch import AsyncElasticsearch - +from .. import AsyncElasticsearch from .connections import Connections diff --git a/elasticsearch/dsl/connections.py b/elasticsearch/dsl/connections.py index 8acd80c6e..679eaaf40 100644 --- a/elasticsearch/dsl/connections.py +++ b/elasticsearch/dsl/connections.py @@ -17,8 +17,7 @@ from typing import Any, Dict, Generic, Type, TypeVar, Union -from elasticsearch import Elasticsearch, __versionstr__ - +from .. import Elasticsearch, __versionstr__ from .serializer import serializer _T = TypeVar("_T") diff --git a/elasticsearch/dsl/pydantic.py b/elasticsearch/dsl/pydantic.py index d1f8efaac..6461a8dd4 100644 --- a/elasticsearch/dsl/pydantic.py +++ b/elasticsearch/dsl/pydantic.py @@ -20,7 +20,7 @@ from pydantic import BaseModel, Field, PrivateAttr from typing_extensions import Annotated, Self, dataclass_transform -from elasticsearch import dsl +from .. import dsl class ESMeta(BaseModel): diff --git a/elasticsearch/dsl/serializer.py b/elasticsearch/dsl/serializer.py index 3080f1dad..641e78f4b 100644 --- a/elasticsearch/dsl/serializer.py +++ b/elasticsearch/dsl/serializer.py @@ -17,8 +17,7 @@ from typing import Any -from elasticsearch.serializer import JSONSerializer - +from ..serializer import JSONSerializer from .utils import AttrList diff --git a/elasticsearch/dsl/utils.py b/elasticsearch/dsl/utils.py index cce3c052c..dd5311b7c 100644 --- a/elasticsearch/dsl/utils.py +++ b/elasticsearch/dsl/utils.py @@ -44,8 +44,7 @@ if TYPE_CHECKING: from elastic_transport import ObjectApiResponse - from elasticsearch import AsyncElasticsearch, Elasticsearch - + from .. import AsyncElasticsearch, Elasticsearch from .document_base import DocumentOptions from .field import Field from .index_base import IndexBase diff --git a/elasticsearch/esql/functions.py b/elasticsearch/esql/functions.py index 46cf5c9d9..3893506bd 100644 --- a/elasticsearch/esql/functions.py +++ b/elasticsearch/esql/functions.py @@ -18,8 +18,8 @@ import json from typing import Any -from elasticsearch.dsl.document_base import InstrumentedExpression -from elasticsearch.esql.esql import ESQLBase, ExpressionType +from ..dsl.document_base import InstrumentedExpression +from ..esql.esql import ESQLBase, ExpressionType def _render(v: Any) -> str: diff --git a/elasticsearch/helpers/vectorstore/__init__.py b/elasticsearch/helpers/vectorstore/__init__.py index 30a4c3d6e..d7874ffe2 100644 --- a/elasticsearch/helpers/vectorstore/__init__.py +++ b/elasticsearch/helpers/vectorstore/__init__.py @@ -15,31 +15,31 @@ # specific language governing permissions and limitations # under the License. -from elasticsearch.helpers.vectorstore._async.embedding_service import ( +from ...helpers.vectorstore._async.embedding_service import ( AsyncElasticsearchEmbeddings, AsyncEmbeddingService, ) -from elasticsearch.helpers.vectorstore._async.strategies import ( +from ...helpers.vectorstore._async.strategies import ( AsyncBM25Strategy, AsyncDenseVectorScriptScoreStrategy, AsyncDenseVectorStrategy, AsyncRetrievalStrategy, AsyncSparseVectorStrategy, ) -from elasticsearch.helpers.vectorstore._async.vectorstore import AsyncVectorStore -from elasticsearch.helpers.vectorstore._sync.embedding_service import ( +from ...helpers.vectorstore._async.vectorstore import AsyncVectorStore +from ...helpers.vectorstore._sync.embedding_service import ( ElasticsearchEmbeddings, EmbeddingService, ) -from elasticsearch.helpers.vectorstore._sync.strategies import ( +from ...helpers.vectorstore._sync.strategies import ( BM25Strategy, DenseVectorScriptScoreStrategy, DenseVectorStrategy, RetrievalStrategy, SparseVectorStrategy, ) -from elasticsearch.helpers.vectorstore._sync.vectorstore import VectorStore -from elasticsearch.helpers.vectorstore._utils import DistanceMetric +from ...helpers.vectorstore._sync.vectorstore import VectorStore +from ...helpers.vectorstore._utils import DistanceMetric __all__ = [ "AsyncBM25Strategy", diff --git a/elasticsearch/helpers/vectorstore/_async/_utils.py b/elasticsearch/helpers/vectorstore/_async/_utils.py index 67b6b6a27..210ef25c7 100644 --- a/elasticsearch/helpers/vectorstore/_async/_utils.py +++ b/elasticsearch/helpers/vectorstore/_async/_utils.py @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -from elasticsearch import AsyncElasticsearch, BadRequestError, NotFoundError +from .... import AsyncElasticsearch, BadRequestError, NotFoundError async def model_must_be_deployed(client: AsyncElasticsearch, model_id: str) -> None: diff --git a/elasticsearch/helpers/vectorstore/_async/embedding_service.py b/elasticsearch/helpers/vectorstore/_async/embedding_service.py index 20005b665..3b779dc26 100644 --- a/elasticsearch/helpers/vectorstore/_async/embedding_service.py +++ b/elasticsearch/helpers/vectorstore/_async/embedding_service.py @@ -18,8 +18,8 @@ from abc import ABC, abstractmethod from typing import List -from elasticsearch import AsyncElasticsearch -from elasticsearch._version import __versionstr__ as lib_version +from .... import AsyncElasticsearch +from ...._version import __versionstr__ as lib_version class AsyncEmbeddingService(ABC): diff --git a/elasticsearch/helpers/vectorstore/_async/strategies.py b/elasticsearch/helpers/vectorstore/_async/strategies.py index 2a52bcb4b..327a53e12 100644 --- a/elasticsearch/helpers/vectorstore/_async/strategies.py +++ b/elasticsearch/helpers/vectorstore/_async/strategies.py @@ -18,9 +18,9 @@ from abc import ABC, abstractmethod from typing import Any, Dict, List, Optional, Tuple, Union, cast -from elasticsearch import AsyncElasticsearch -from elasticsearch.helpers.vectorstore._async._utils import model_must_be_deployed -from elasticsearch.helpers.vectorstore._utils import DistanceMetric +from .... import AsyncElasticsearch +from ....helpers.vectorstore._async._utils import model_must_be_deployed +from ....helpers.vectorstore._utils import DistanceMetric class AsyncRetrievalStrategy(ABC): diff --git a/elasticsearch/helpers/vectorstore/_async/vectorstore.py b/elasticsearch/helpers/vectorstore/_async/vectorstore.py index 3b8c1e9e9..71a2274a0 100644 --- a/elasticsearch/helpers/vectorstore/_async/vectorstore.py +++ b/elasticsearch/helpers/vectorstore/_async/vectorstore.py @@ -19,14 +19,14 @@ import uuid from typing import Any, Callable, Dict, List, Optional -from elasticsearch import AsyncElasticsearch -from elasticsearch._version import __versionstr__ as lib_version -from elasticsearch.helpers import BulkIndexError, async_bulk -from elasticsearch.helpers.vectorstore import ( +from .... import AsyncElasticsearch +from ...._version import __versionstr__ as lib_version +from ....helpers import BulkIndexError, async_bulk +from ....helpers.vectorstore import ( AsyncEmbeddingService, AsyncRetrievalStrategy, ) -from elasticsearch.helpers.vectorstore._utils import maximal_marginal_relevance +from ....helpers.vectorstore._utils import maximal_marginal_relevance logger = logging.getLogger(__name__) diff --git a/elasticsearch/helpers/vectorstore/_sync/_utils.py b/elasticsearch/helpers/vectorstore/_sync/_utils.py index 496aec970..b6e1b66f5 100644 --- a/elasticsearch/helpers/vectorstore/_sync/_utils.py +++ b/elasticsearch/helpers/vectorstore/_sync/_utils.py @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -from elasticsearch import BadRequestError, Elasticsearch, NotFoundError +from .... import BadRequestError, Elasticsearch, NotFoundError def model_must_be_deployed(client: Elasticsearch, model_id: str) -> None: diff --git a/elasticsearch/helpers/vectorstore/_sync/embedding_service.py b/elasticsearch/helpers/vectorstore/_sync/embedding_service.py index 5b0163d98..b597cdd71 100644 --- a/elasticsearch/helpers/vectorstore/_sync/embedding_service.py +++ b/elasticsearch/helpers/vectorstore/_sync/embedding_service.py @@ -18,8 +18,8 @@ from abc import ABC, abstractmethod from typing import List -from elasticsearch import Elasticsearch -from elasticsearch._version import __versionstr__ as lib_version +from .... import Elasticsearch +from ...._version import __versionstr__ as lib_version class EmbeddingService(ABC): diff --git a/elasticsearch/helpers/vectorstore/_sync/strategies.py b/elasticsearch/helpers/vectorstore/_sync/strategies.py index af89edf8d..0cb86a30f 100644 --- a/elasticsearch/helpers/vectorstore/_sync/strategies.py +++ b/elasticsearch/helpers/vectorstore/_sync/strategies.py @@ -18,9 +18,9 @@ from abc import ABC, abstractmethod from typing import Any, Dict, List, Optional, Tuple, Union, cast -from elasticsearch import Elasticsearch -from elasticsearch.helpers.vectorstore._sync._utils import model_must_be_deployed -from elasticsearch.helpers.vectorstore._utils import DistanceMetric +from .... import Elasticsearch +from ....helpers.vectorstore._sync._utils import model_must_be_deployed +from ....helpers.vectorstore._utils import DistanceMetric class RetrievalStrategy(ABC): diff --git a/elasticsearch/helpers/vectorstore/_sync/vectorstore.py b/elasticsearch/helpers/vectorstore/_sync/vectorstore.py index 6a6a5ee2a..bbb159bb6 100644 --- a/elasticsearch/helpers/vectorstore/_sync/vectorstore.py +++ b/elasticsearch/helpers/vectorstore/_sync/vectorstore.py @@ -19,14 +19,14 @@ import uuid from typing import Any, Callable, Dict, List, Optional -from elasticsearch import Elasticsearch -from elasticsearch._version import __versionstr__ as lib_version -from elasticsearch.helpers import BulkIndexError, bulk -from elasticsearch.helpers.vectorstore import ( +from .... import Elasticsearch +from ...._version import __versionstr__ as lib_version +from ....helpers import BulkIndexError, bulk +from ....helpers.vectorstore import ( EmbeddingService, RetrievalStrategy, ) -from elasticsearch.helpers.vectorstore._utils import maximal_marginal_relevance +from ....helpers.vectorstore._utils import maximal_marginal_relevance logger = logging.getLogger(__name__) From ce354e05fa525874d99eeba1b0b24264473a2de8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 11:18:09 +0000 Subject: [PATCH 35/36] explicit positional arguments in DSL generated classes (#3233) (#3239) (cherry picked from commit e8f2d04f867d4787bf37fa027fca248d8458c2cb) Co-authored-by: Miguel Grinberg --- elasticsearch/dsl/query.py | 23 +++++++++++++++++++++++ elasticsearch/dsl/types.py | 2 ++ utils/templates/aggs.py.tpl | 3 +++ utils/templates/query.py.tpl | 3 +++ utils/templates/types.py.tpl | 3 +++ 5 files changed, 34 insertions(+) diff --git a/elasticsearch/dsl/query.py b/elasticsearch/dsl/query.py index 927af6ad4..d350b70ce 100644 --- a/elasticsearch/dsl/query.py +++ b/elasticsearch/dsl/query.py @@ -370,6 +370,7 @@ def __init__( _value: Union[ "types.CommonTermsQuery", Dict[str, Any], "DefaultType" ] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -666,6 +667,7 @@ def __init__( self, _field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, _value: Union["types.FuzzyQuery", Dict[str, Any], "DefaultType"] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -708,6 +710,7 @@ def __init__( Dict[str, Any], "DefaultType", ] = DEFAULT, + /, *, type: Union[Literal["memory", "indexed"], "DefaultType"] = DEFAULT, validation_method: Union[ @@ -771,6 +774,7 @@ def __init__( Dict[str, Any], "DefaultType", ] = DEFAULT, + /, *, distance: Union[str, "DefaultType"] = DEFAULT, distance_type: Union[Literal["arc", "plane"], "DefaultType"] = DEFAULT, @@ -810,6 +814,7 @@ def __init__( self, _field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, _value: Union["types.GeoGridQuery", Dict[str, Any], "DefaultType"] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -839,6 +844,7 @@ def __init__( _value: Union[ "types.GeoPolygonPoints", Dict[str, Any], "DefaultType" ] = DEFAULT, + /, *, validation_method: Union[ Literal["coerce", "ignore_malformed", "strict"], "DefaultType" @@ -885,6 +891,7 @@ def __init__( _value: Union[ "types.GeoShapeFieldQuery", Dict[str, Any], "DefaultType" ] = DEFAULT, + /, *, ignore_unmapped: Union[bool, "DefaultType"] = DEFAULT, boost: Union[float, "DefaultType"] = DEFAULT, @@ -1060,6 +1067,7 @@ def __init__( self, _field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, _value: Union["types.IntervalsQuery", Dict[str, Any], "DefaultType"] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -1151,6 +1159,7 @@ def __init__( self, _field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, _value: Union["types.MatchQuery", Dict[str, Any], "DefaultType"] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -1216,6 +1225,7 @@ def __init__( _value: Union[ "types.MatchBoolPrefixQuery", Dict[str, Any], "DefaultType" ] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -1276,6 +1286,7 @@ def __init__( _value: Union[ "types.MatchPhraseQuery", Dict[str, Any], "DefaultType" ] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -1301,6 +1312,7 @@ def __init__( _value: Union[ "types.MatchPhrasePrefixQuery", Dict[str, Any], "DefaultType" ] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -1784,6 +1796,7 @@ def __init__( self, _field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, _value: Union["types.PrefixQuery", Dict[str, Any], "DefaultType"] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -1950,6 +1963,7 @@ def __init__( self, _field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, _value: Union["wrappers.Range[Any]", Dict[str, Any], "DefaultType"] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -2028,6 +2042,7 @@ def __init__( self, _field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, _value: Union["types.RegexpQuery", Dict[str, Any], "DefaultType"] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -2198,6 +2213,7 @@ def __init__( self, _field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, _value: Union["types.ShapeFieldQuery", Dict[str, Any], "DefaultType"] = DEFAULT, + /, *, ignore_unmapped: Union[bool, "DefaultType"] = DEFAULT, boost: Union[float, "DefaultType"] = DEFAULT, @@ -2556,6 +2572,7 @@ def __init__( self, _field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, _value: Union["types.SpanTermQuery", Dict[str, Any], "DefaultType"] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -2676,6 +2693,7 @@ def __init__( self, _field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, _value: Union["types.TermQuery", Dict[str, Any], "DefaultType"] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -2710,6 +2728,7 @@ def __init__( Dict[str, Any], "DefaultType", ] = DEFAULT, + /, *, boost: Union[float, "DefaultType"] = DEFAULT, _name: Union[str, "DefaultType"] = DEFAULT, @@ -2743,6 +2762,7 @@ def __init__( self, _field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, _value: Union["types.TermsSetQuery", Dict[str, Any], "DefaultType"] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -2768,6 +2788,7 @@ def __init__( _value: Union[ "types.TextExpansionQuery", Dict[str, Any], "DefaultType" ] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -2792,6 +2813,7 @@ def __init__( _value: Union[ "types.WeightedTokensQuery", Dict[str, Any], "DefaultType" ] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: @@ -2813,6 +2835,7 @@ def __init__( self, _field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, _value: Union["types.WildcardQuery", Dict[str, Any], "DefaultType"] = DEFAULT, + /, **kwargs: Any, ): if _field is not DEFAULT: diff --git a/elasticsearch/dsl/types.py b/elasticsearch/dsl/types.py index f2ecca427..35cc544f5 100644 --- a/elasticsearch/dsl/types.py +++ b/elasticsearch/dsl/types.py @@ -941,6 +941,7 @@ def __init__( Dict[str, Any], "DefaultType", ] = DEFAULT, + /, *, mode: Union[ Literal["min", "max", "sum", "avg", "median"], DefaultType @@ -3272,6 +3273,7 @@ def __init__( self, _field: Union[str, "InstrumentedField", "DefaultType"] = DEFAULT, _value: Union["FieldSort", Dict[str, Any], "DefaultType"] = DEFAULT, + /, *, _score: Union["ScoreSort", Dict[str, Any], DefaultType] = DEFAULT, _doc: Union["ScoreSort", Dict[str, Any], DefaultType] = DEFAULT, diff --git a/utils/templates/aggs.py.tpl b/utils/templates/aggs.py.tpl index 68d46e63d..31e27bf5f 100644 --- a/utils/templates/aggs.py.tpl +++ b/utils/templates/aggs.py.tpl @@ -255,6 +255,9 @@ class {{ k.name }}({{ k.parent if k.parent else parent }}[_R]): {{ arg.name }}: {{ arg.type }} = DEFAULT, {% endif %} {% endfor %} + {% if k.args and k.args[0].positional %} + /, + {% endif %} {% if k.args and not k.args[-1].positional %} *, {% endif %} diff --git a/utils/templates/query.py.tpl b/utils/templates/query.py.tpl index 6816f2d07..2ca767487 100644 --- a/utils/templates/query.py.tpl +++ b/utils/templates/query.py.tpl @@ -185,6 +185,9 @@ class {{ k.name }}({{ parent }}): {{ arg.name }}: {{ arg.type }} = DEFAULT, {% endif %} {% endfor %} + {% if k.args and k.args[0].positional %} + /, + {% endif %} {% if k.args and not k.args[-1].positional %} *, {% endif %} diff --git a/utils/templates/types.py.tpl b/utils/templates/types.py.tpl index 4ee80d5cb..1cd1e9496 100644 --- a/utils/templates/types.py.tpl +++ b/utils/templates/types.py.tpl @@ -60,6 +60,9 @@ class {{ k.name }}({{ k.parent if k.parent else "AttrDict[Any]" }}): {{ arg.name }}: {{ arg.type }} = DEFAULT, {% endif %} {% endfor %} + {% if k.args and k.args[0].positional %} + /, + {% endif %} {% if k.args and not k.args[-1].positional %} *, {% endif %} From 4c1da18542e1bfa452fc1465e6783a260b98d0bf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 15:09:35 +0000 Subject: [PATCH 36/36] Fix recursive errors generated in transport test (#3240) (#3243) (cherry picked from commit 8143117af8e2c18b5b9d9a63246f996c0283b228) Co-authored-by: Miguel Grinberg --- test_elasticsearch/test_transport.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/test_elasticsearch/test_transport.py b/test_elasticsearch/test_transport.py index 237f21ac1..b64d9d332 100644 --- a/test_elasticsearch/test_transport.py +++ b/test_elasticsearch/test_transport.py @@ -43,7 +43,7 @@ class DummyNode(BaseNode): def __init__(self, config: NodeConfig): self.resp_status = config._extras.pop("status", 200) - self.resp_error = config._extras.pop("exception", None) + self.resp_error = config._extras.pop("exception_factory", None) self.resp_data = config._extras.pop("data", b"{}") self.resp_headers = config._extras.pop( "headers", {"X-elastic-product": "Elasticsearch"} @@ -55,7 +55,7 @@ def __init__(self, config: NodeConfig): def perform_request(self, *args, **kwargs): self.calls.append((args, kwargs)) if self.resp_error: - raise self.resp_error + raise self.resp_error() return NodeApiResponse( ApiResponseMeta( status=self.resp_status, @@ -310,7 +310,9 @@ def test_request_will_fail_after_x_retries(self): "http", "localhost", 9200, - _extras={"exception": ConnectionError("abandon ship!")}, + _extras={ + "exception_factory": lambda: ConnectionError("abandon ship!") + }, ) ], node_class=DummyNode, @@ -335,13 +337,17 @@ def test_failed_connection_will_be_marked_as_dead(self): "http", "localhost", 9200, - _extras={"exception": ConnectionError("abandon ship!")}, + _extras={ + "exception_factory": lambda: ConnectionError("abandon ship!") + }, ), NodeConfig( "http", "localhost", 9201, - _extras={"exception": ConnectionError("abandon ship!")}, + _extras={ + "exception_factory": lambda: ConnectionError("abandon ship!") + }, ), ], node_class=DummyNode,

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 From f9fea5b258efa1e54e1b77fa1db7458b9b8ea61d Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Fri, 24 Oct 2025 19:18:23 +0100 Subject: [PATCH 13/36] Auto-generated code for 9.2 (#3121) * auto-generated code for 9.2 * streams and project namespaces * update buildkite to use 9.2 snapshot build * reran the generator * update schema version * more references to new namespaces --- .buildkite/pipeline.yml | 2 +- docs/sphinx/api/indices.rst | 2 +- docs/sphinx/api/project.rst | 9 + docs/sphinx/api/streams.rst | 9 + docs/sphinx/es_api.rst | 2 + elasticsearch/_async/client/__init__.py | 130 +++-- elasticsearch/_async/client/async_search.py | 7 + elasticsearch/_async/client/cat.py | 515 +++++++++++++++++++- elasticsearch/_async/client/connector.py | 6 +- elasticsearch/_async/client/eql.py | 7 + elasticsearch/_async/client/esql.py | 29 +- elasticsearch/_async/client/fleet.py | 6 +- elasticsearch/_async/client/graph.py | 6 +- elasticsearch/_async/client/ilm.py | 12 +- elasticsearch/_async/client/indices.py | 74 +-- elasticsearch/_async/client/inference.py | 261 +++++----- elasticsearch/_async/client/ingest.py | 8 + elasticsearch/_async/client/license.py | 6 +- elasticsearch/_async/client/logstash.py | 4 +- elasticsearch/_async/client/ml.py | 4 +- elasticsearch/_async/client/nodes.py | 8 +- elasticsearch/_async/client/project.py | 67 +++ elasticsearch/_async/client/security.py | 39 ++ elasticsearch/_async/client/shutdown.py | 20 +- elasticsearch/_async/client/slm.py | 6 +- elasticsearch/_async/client/sql.py | 7 + elasticsearch/_async/client/streams.py | 185 +++++++ elasticsearch/_async/client/watcher.py | 6 +- elasticsearch/_sync/client/__init__.py | 130 +++-- elasticsearch/_sync/client/async_search.py | 7 + elasticsearch/_sync/client/cat.py | 515 +++++++++++++++++++- elasticsearch/_sync/client/connector.py | 6 +- elasticsearch/_sync/client/eql.py | 7 + elasticsearch/_sync/client/esql.py | 29 +- elasticsearch/_sync/client/fleet.py | 6 +- elasticsearch/_sync/client/graph.py | 6 +- elasticsearch/_sync/client/ilm.py | 12 +- elasticsearch/_sync/client/indices.py | 74 +-- elasticsearch/_sync/client/inference.py | 261 +++++----- elasticsearch/_sync/client/ingest.py | 8 + elasticsearch/_sync/client/license.py | 6 +- elasticsearch/_sync/client/logstash.py | 4 +- elasticsearch/_sync/client/ml.py | 4 +- elasticsearch/_sync/client/nodes.py | 8 +- elasticsearch/_sync/client/project.py | 67 +++ elasticsearch/_sync/client/security.py | 39 ++ elasticsearch/_sync/client/shutdown.py | 20 +- elasticsearch/_sync/client/slm.py | 6 +- elasticsearch/_sync/client/sql.py | 7 + elasticsearch/_sync/client/streams.py | 185 +++++++ elasticsearch/_sync/client/watcher.py | 6 +- elasticsearch/_version.py | 1 + elasticsearch/client.py | 4 + elasticsearch/dsl/aggs.py | 97 ++++ elasticsearch/dsl/field.py | 9 +- elasticsearch/dsl/query.py | 6 +- elasticsearch/dsl/response/__init__.py | 3 + elasticsearch/dsl/types.py | 213 +++++++- pyproject.toml | 2 +- 59 files changed, 2573 insertions(+), 612 deletions(-) create mode 100644 docs/sphinx/api/project.rst create mode 100644 docs/sphinx/api/streams.rst create mode 100644 elasticsearch/_async/client/project.py create mode 100644 elasticsearch/_async/client/streams.py create mode 100644 elasticsearch/_sync/client/project.py create mode 100644 elasticsearch/_sync/client/streams.py diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 33eb810a0..71154ec4a 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -5,7 +5,7 @@ steps: env: PYTHON_VERSION: "{{ matrix.python }}" TEST_SUITE: "platinum" - STACK_VERSION: "9.1.0-SNAPSHOT" + STACK_VERSION: "9.2.0-SNAPSHOT" PYTHON_CONNECTION_CLASS: "{{ matrix.connection }}" NOX_SESSION: "{{ matrix.nox_session }}" matrix: diff --git a/docs/sphinx/api/indices.rst b/docs/sphinx/api/indices.rst index 9523a3558..8d310a3b8 100644 --- a/docs/sphinx/api/indices.rst +++ b/docs/sphinx/api/indices.rst @@ -6,4 +6,4 @@ Indices :no-index: .. autoclass:: IndicesClient - :members: \ No newline at end of file + :members: diff --git a/docs/sphinx/api/project.rst b/docs/sphinx/api/project.rst new file mode 100644 index 000000000..0ab68dc2d --- /dev/null +++ b/docs/sphinx/api/project.rst @@ -0,0 +1,9 @@ +.. _project: + +Project +------- +.. py:module:: elasticsearch.client + :no-index: + +.. autoclass:: ProjectClient + :members: diff --git a/docs/sphinx/api/streams.rst b/docs/sphinx/api/streams.rst new file mode 100644 index 000000000..d5f45b6e7 --- /dev/null +++ b/docs/sphinx/api/streams.rst @@ -0,0 +1,9 @@ +.. _streams: + +Streams +------- +.. py:module:: elasticsearch.client + :no-index: + +.. autoclass:: StreamsClient + :members: diff --git a/docs/sphinx/es_api.rst b/docs/sphinx/es_api.rst index d246ec6f5..5d42e7d2c 100644 --- a/docs/sphinx/es_api.rst +++ b/docs/sphinx/es_api.rst @@ -39,6 +39,7 @@ arguments are required for all calls. api/ml api/monitoring api/nodes + api/project api/query-rules api/rollup-indices api/search-application @@ -50,6 +51,7 @@ arguments are required for all calls. api/snapshots api/snapshottable-features api/sql + api/streams api/synonyms api/tls-ssl api/tasks diff --git a/elasticsearch/_async/client/__init__.py b/elasticsearch/_async/client/__init__.py index e4fee64cc..8c7466a45 100644 --- a/elasticsearch/_async/client/__init__.py +++ b/elasticsearch/_async/client/__init__.py @@ -63,6 +63,7 @@ from .ml import MlClient from .monitoring import MonitoringClient from .nodes import NodesClient +from .project import ProjectClient from .query_rules import QueryRulesClient from .rollup import RollupClient from .search_application import SearchApplicationClient @@ -74,6 +75,7 @@ from .snapshot import SnapshotClient from .sql import SqlClient from .ssl import SslClient +from .streams import StreamsClient from .synonyms import SynonymsClient from .tasks import TasksClient from .text_structure import TextStructureClient @@ -368,6 +370,7 @@ def __init__( self.migration = MigrationClient(self) self.ml = MlClient(self) self.monitoring = MonitoringClient(self) + self.project = ProjectClient(self) self.query_rules = QueryRulesClient(self) self.rollup = RollupClient(self) self.search_application = SearchApplicationClient(self) @@ -378,6 +381,7 @@ def __init__( self.shutdown = ShutdownClient(self) self.sql = SqlClient(self) self.ssl = SslClient(self) + self.streams = StreamsClient(self) self.synonyms = SynonymsClient(self) self.text_structure = TextStructureClient(self) self.transform = TransformClient(self) @@ -845,11 +849,7 @@ async def close_point_in_time( if not __body: if id is not None: __body["id"] = id - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "DELETE", __path, @@ -889,6 +889,7 @@ async def count( min_score: t.Optional[float] = None, preference: t.Optional[str] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, q: t.Optional[str] = None, query: t.Optional[t.Mapping[str, t.Any]] = None, routing: t.Optional[str] = None, @@ -922,8 +923,8 @@ async def count( This parameter can be used only when the `q` query string parameter is specified. :param analyzer: The analyzer to use for the query string. This parameter can be used only when the `q` query string parameter is specified. - :param default_operator: The default operator for query string query: `AND` or - `OR`. This parameter can be used only when the `q` query string parameter + :param default_operator: The default operator for query string query: `and` or + `or`. This parameter can be used only when the `q` query string parameter is specified. :param df: The field to use as a default when no field prefix is given in the query string. This parameter can be used only when the `q` query string parameter @@ -943,6 +944,10 @@ async def count( in the result. :param preference: The node or shard the operation should be performed on. By default, it is random. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param q: The query in Lucene query string syntax. This parameter cannot be used with a request body. :param query: Defines the search query using Query DSL. A request body query @@ -995,6 +1000,8 @@ async def count( __query["preference"] = preference if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if q is not None: __query["q"] = q if routing is not None: @@ -1044,7 +1051,7 @@ async def create( timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, wait_for_active_shards: t.Optional[ t.Union[int, t.Union[str, t.Literal["all", "index-setting"]]] @@ -1223,7 +1230,7 @@ async def delete( timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, wait_for_active_shards: t.Optional[ t.Union[int, t.Union[str, t.Literal["all", "index-setting"]]] @@ -1468,8 +1475,8 @@ async def delete_by_query( used only when the `q` query string parameter is specified. :param conflicts: What to do if delete by query hits version conflicts: `abort` or `proceed`. - :param default_operator: The default operator for query string query: `AND` or - `OR`. This parameter can be used only when the `q` query string parameter + :param default_operator: The default operator for query string query: `and` or + `or`. This parameter can be used only when the `q` query string parameter is specified. :param df: The field to use as default where no field prefix is given in the query string. This parameter can be used only when the `q` query string parameter @@ -1763,7 +1770,7 @@ async def exists( stored_fields: t.Optional[t.Union[str, t.Sequence[str]]] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, ) -> HeadApiResponse: """ @@ -1892,7 +1899,7 @@ async def exists_source( source_includes: t.Optional[t.Union[str, t.Sequence[str]]] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, ) -> HeadApiResponse: """ @@ -2021,8 +2028,8 @@ async def explain( This parameter can be used only when the `q` query string parameter is specified. :param analyzer: The analyzer to use for the query string. This parameter can be used only when the `q` query string parameter is specified. - :param default_operator: The default operator for query string query: `AND` or - `OR`. This parameter can be used only when the `q` query string parameter + :param default_operator: The default operator for query string query: `and` or + `or`. This parameter can be used only when the `q` query string parameter is specified. :param df: The field to use as default where no field prefix is given in the query string. This parameter can be used only when the `q` query string parameter @@ -2133,6 +2140,7 @@ async def field_caps( include_unmapped: t.Optional[bool] = None, index_filter: t.Optional[t.Mapping[str, t.Any]] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, types: t.Optional[t.Sequence[str]] = None, body: t.Optional[t.Dict[str, t.Any]] = None, @@ -2176,6 +2184,11 @@ async def field_caps( deleted documents) are outside of the provided range. However, not all queries can rewrite to `match_none` so this API may return an index even if the provided filter matches no document. + :param project_routing: Specifies a subset of projects to target for the field-caps + query using project metadata tags in a subset of Lucene query syntax. Allowed + Lucene queries: the _alias tag and a single value (possibly wildcarded). + Examples: _alias:my-project _alias:_origin _alias:*pr* Supported in serverless + only. :param runtime_mappings: Define ad-hoc runtime fields in the request similar to the way it is done in search requests. These fields exist only as part of the query and take precedence over fields defined with the same name in @@ -2213,6 +2226,8 @@ async def field_caps( __query["include_unmapped"] = include_unmapped if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if types is not None: __query["types"] = types if not __body: @@ -2266,7 +2281,7 @@ async def get( stored_fields: t.Optional[t.Union[str, t.Sequence[str]]] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, ) -> ObjectApiResponse[t.Any]: """ @@ -2556,7 +2571,7 @@ async def get_source( source_includes: t.Optional[t.Union[str, t.Sequence[str]]] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, ) -> ObjectApiResponse[t.Any]: """ @@ -2736,7 +2751,7 @@ async def index( timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, wait_for_active_shards: t.Optional[ t.Union[int, t.Union[str, t.Literal["all", "index-setting"]]] @@ -3151,6 +3166,7 @@ async def msearch( max_concurrent_shard_requests: t.Optional[int] = None, pre_filter_shard_size: t.Optional[int] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, rest_total_hits_as_int: t.Optional[bool] = None, routing: t.Optional[str] = None, search_type: t.Optional[ @@ -3212,6 +3228,10 @@ async def msearch( roundtrip can limit the number of shards significantly if for instance a shard can not match any documents based on its rewrite method i.e., if date filters are mandatory to match but the shard bounds and the query are disjoint. + :param project_routing: Specifies a subset of projects to target for a search + using project metadata tags in a subset Lucene syntax. Allowed Lucene queries: + the _alias tag and a single value (possible wildcarded). Examples: _alias:my-project + _alias:_origin _alias:*pr* Supported in serverless only. :param rest_total_hits_as_int: If true, hits.total are returned as an integer in the response. Defaults to false, which returns an object. :param routing: Custom routing value used to route search operations to a specific @@ -3261,6 +3281,8 @@ async def msearch( __query["pre_filter_shard_size"] = pre_filter_shard_size if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if rest_total_hits_as_int is not None: __query["rest_total_hits_as_int"] = rest_total_hits_as_int if routing is not None: @@ -3299,6 +3321,7 @@ async def msearch_template( human: t.Optional[bool] = None, max_concurrent_searches: t.Optional[int] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, rest_total_hits_as_int: t.Optional[bool] = None, search_type: t.Optional[ t.Union[str, t.Literal["dfs_query_then_fetch", "query_then_fetch"]] @@ -3332,6 +3355,10 @@ async def msearch_template( for cross-cluster search requests. :param max_concurrent_searches: The maximum number of concurrent searches the API can run. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param rest_total_hits_as_int: If `true`, the response returns `hits.total` as an integer. If `false`, it returns `hits.total` as an object. :param search_type: The type of the search operation. @@ -3364,6 +3391,8 @@ async def msearch_template( __query["max_concurrent_searches"] = max_concurrent_searches if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if rest_total_hits_as_int is not None: __query["rest_total_hits_as_int"] = rest_total_hits_as_int if search_type is not None: @@ -3409,7 +3438,7 @@ async def mtermvectors( term_statistics: t.Optional[bool] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, body: t.Optional[t.Dict[str, t.Any]] = None, ) -> ObjectApiResponse[t.Any]: @@ -3534,6 +3563,7 @@ async def open_point_in_time( max_concurrent_shard_requests: t.Optional[int] = None, preference: t.Optional[str] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, routing: t.Optional[str] = None, body: t.Optional[t.Dict[str, t.Any]] = None, ) -> ObjectApiResponse[t.Any]: @@ -3590,6 +3620,11 @@ async def open_point_in_time( that each sub-search request executes per node. :param preference: The node or shard the operation should be performed on. By default, it is random. + :param project_routing: Specifies a subset of projects to target for the PIT + request using project metadata tags in a subset of Lucene query syntax. Allowed + Lucene queries: the _alias tag and a single value (possibly wildcarded). + Examples: _alias:my-project _alias:_origin _alias:*pr* Supported in serverless + only. :param routing: A custom value that is used to route operations to a specific shard. """ @@ -3621,6 +3656,8 @@ async def open_point_in_time( __query["preference"] = preference if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if routing is not None: __query["routing"] = routing if not __body: @@ -3819,7 +3856,7 @@ async def rank_eval( ) @_rewrite_parameters( - body_fields=("dest", "source", "conflicts", "max_docs", "script", "size"), + body_fields=("dest", "source", "conflicts", "max_docs", "script"), ) async def reindex( self, @@ -3837,7 +3874,6 @@ async def reindex( require_alias: t.Optional[bool] = None, script: t.Optional[t.Mapping[str, t.Any]] = None, scroll: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, - size: t.Optional[int] = None, slices: t.Optional[t.Union[int, t.Union[str, t.Literal["auto"]]]] = None, timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, wait_for_active_shards: t.Optional[ @@ -3913,7 +3949,6 @@ async def reindex( reindexing. :param scroll: The period of time that a consistent view of the index should be maintained for scrolled search. - :param size: :param slices: The number of slices this task should be divided into. It defaults to one slice, which means the task isn't sliced into subtasks. Reindex supports sliced scroll to parallelize the reindexing process. This parallelization @@ -3978,8 +4013,6 @@ async def reindex( __body["max_docs"] = max_docs if script is not None: __body["script"] = script - if size is not None: - __body["size"] = size __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", @@ -4106,11 +4139,7 @@ async def render_search_template( __body["params"] = params if source is not None: __body["source"] = source - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", __path, @@ -4193,11 +4222,7 @@ async def scripts_painless_execute( __body["context_setup"] = context_setup if script is not None: __body["script"] = script - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", __path, @@ -4377,6 +4402,7 @@ async def search( preference: t.Optional[str] = None, pretty: t.Optional[bool] = None, profile: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, q: t.Optional[str] = None, query: t.Optional[t.Mapping[str, t.Any]] = None, rank: t.Optional[t.Mapping[str, t.Any]] = None, @@ -4474,8 +4500,8 @@ async def search( node and the remote clusters are minimized when running cross-cluster search (CCS) requests. :param collapse: Collapses search results the values of the specified field. - :param default_operator: The default operator for the query string query: `AND` - or `OR`. This parameter can be used only when the `q` query string parameter + :param default_operator: The default operator for the query string query: `and` + or `or`. This parameter can be used only when the `q` query string parameter is specified. :param df: The field to use as a default when no field prefix is given in the query string. This parameter can be used only when the `q` query string parameter @@ -4560,6 +4586,10 @@ async def search( :param profile: Set to `true` to return detailed timing information about the execution of individual components in a search request. NOTE: This is a debugging tool and adds significant overhead to search execution. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param q: A query in the Lucene query string syntax. Query parameter searches do not support the full Elasticsearch Query DSL but are handy for testing. IMPORTANT: This parameter overrides the query parameter in the request body. @@ -4714,6 +4744,8 @@ async def search( __query["preference"] = preference if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if q is not None: __query["q"] = q if request_cache is not None: @@ -4868,6 +4900,7 @@ async def search_mvt( ] = None, human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, query: t.Optional[t.Mapping[str, t.Any]] = None, runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, size: t.Optional[int] = None, @@ -5185,6 +5218,10 @@ async def search_mvt( In the aggs layer, each feature represents a `geotile_grid` cell. If `grid, each feature is a polygon of the cells bounding box. If `point`, each feature is a Point that is the centroid of the cell. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param query: The query DSL used to filter documents for the search. :param runtime_mappings: Defines one or more runtime fields in the search request. These fields take precedence over mapped fields with the same name. @@ -5248,6 +5285,8 @@ async def search_mvt( __query["human"] = human if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if not __body: if aggs is not None: __body["aggs"] = aggs @@ -5421,6 +5460,7 @@ async def search_template( preference: t.Optional[str] = None, pretty: t.Optional[bool] = None, profile: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, rest_total_hits_as_int: t.Optional[bool] = None, routing: t.Optional[str] = None, scroll: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, @@ -5466,6 +5506,10 @@ async def search_template( :param preference: The node or shard the operation should be performed on. It is random by default. :param profile: If `true`, the query execution is profiled. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param rest_total_hits_as_int: If `true`, `hits.total` is rendered as an integer in the response. If `false`, it is rendered as an object. :param routing: A custom value used to route operations to a specific shard. @@ -5507,6 +5551,8 @@ async def search_template( __query["preference"] = preference if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if rest_total_hits_as_int is not None: __query["rest_total_hits_as_int"] = rest_total_hits_as_int if routing is not None: @@ -5633,11 +5679,7 @@ async def terms_enum( __body["string"] = string if timeout is not None: __body["timeout"] = timeout - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", __path, @@ -5687,7 +5729,7 @@ async def termvectors( term_statistics: t.Optional[bool] = None, version: t.Optional[int] = None, version_type: t.Optional[ - t.Union[str, t.Literal["external", "external_gte", "force", "internal"]] + t.Union[str, t.Literal["external", "external_gte", "internal"]] ] = None, body: t.Optional[t.Dict[str, t.Any]] = None, ) -> ObjectApiResponse[t.Any]: @@ -6163,8 +6205,8 @@ async def update_by_query( be used only when the `q` query string parameter is specified. :param conflicts: The preferred behavior when update by query hits version conflicts: `abort` or `proceed`. - :param default_operator: The default operator for query string query: `AND` or - `OR`. This parameter can be used only when the `q` query string parameter + :param default_operator: The default operator for query string query: `and` or + `or`. This parameter can be used only when the `q` query string parameter is specified. :param df: The field to use as default where no field prefix is given in the query string. This parameter can be used only when the `q` query string parameter diff --git a/elasticsearch/_async/client/async_search.py b/elasticsearch/_async/client/async_search.py index 4d1c32974..5ebe24cd0 100644 --- a/elasticsearch/_async/client/async_search.py +++ b/elasticsearch/_async/client/async_search.py @@ -287,6 +287,7 @@ async def submit( preference: t.Optional[str] = None, pretty: t.Optional[bool] = None, profile: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, q: t.Optional[str] = None, query: t.Optional[t.Mapping[str, t.Any]] = None, request_cache: t.Optional[bool] = None, @@ -408,6 +409,10 @@ async def submit( :param preference: Specify the node or shard the operation should be performed on (default: random) :param profile: + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param q: Query in the Lucene query string syntax :param query: Defines the search definition using the Query DSL. :param request_cache: Specify if request cache should be used for this request @@ -528,6 +533,8 @@ async def submit( __query["preference"] = preference if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if q is not None: __query["q"] = q if request_cache is not None: diff --git a/elasticsearch/_async/client/cat.py b/elasticsearch/_async/client/cat.py index 84f946f5c..f033a97ec 100644 --- a/elasticsearch/_async/client/cat.py +++ b/elasticsearch/_async/client/cat.py @@ -36,6 +36,9 @@ async def aliases( self, *, name: t.Optional[t.Union[str, t.Sequence[str]]] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, expand_wildcards: t.Optional[ t.Union[ @@ -80,6 +83,9 @@ async def aliases( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -95,6 +101,14 @@ async def aliases( :param name: A comma-separated list of aliases to retrieve. Supports wildcards (`*`). To retrieve all aliases, omit this parameter or use `*` or `_all`. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param expand_wildcards: The type of index that wildcard patterns can match. If the request can target data streams, this argument determines whether wildcard expressions match hidden data streams. It supports comma-separated @@ -112,6 +126,12 @@ async def aliases( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -122,6 +142,8 @@ async def aliases( __path_parts = {} __path = "/_cat/aliases" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if expand_wildcards is not None: @@ -142,6 +164,8 @@ async def aliases( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -213,6 +237,9 @@ async def allocation( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -227,7 +254,14 @@ async def allocation( :param node_id: A comma-separated list of node identifiers or names used to limit the returned information. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -242,6 +276,12 @@ async def allocation( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -274,6 +314,8 @@ async def allocation( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -291,6 +333,9 @@ async def component_templates( self, *, name: t.Optional[str] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -330,6 +375,9 @@ async def component_templates( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -346,6 +394,14 @@ async def component_templates( :param name: The name of the component template. It accepts wildcard expressions. If it is omitted, all component templates are returned. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -360,6 +416,12 @@ async def component_templates( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -370,6 +432,8 @@ async def component_templates( __path_parts = {} __path = "/_cat/component_templates" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -390,6 +454,8 @@ async def component_templates( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -407,6 +473,9 @@ async def count( self, *, index: t.Optional[t.Union[str, t.Sequence[str]]] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -419,7 +488,11 @@ async def count( help: t.Optional[bool] = None, human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -437,15 +510,33 @@ async def count( :param index: A comma-separated list of data streams, indices, and aliases used to limit the request. It supports wildcards (`*`). To target all data streams and indices, omit this parameter or use `*` or `_all`. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple wildcards. :param help: When set to `true` will output available columns. This option can't be combined with any other query string option. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -456,6 +547,8 @@ async def count( __path_parts = {} __path = "/_cat/count" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -470,8 +563,12 @@ async def count( __query["human"] = human if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -507,6 +604,9 @@ async def fielddata( human: t.Optional[bool] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -522,7 +622,14 @@ async def fielddata( :param fields: Comma-separated list of fields used to limit returned information. To retrieve all fields, omit this parameter. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -532,6 +639,12 @@ async def fielddata( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -560,6 +673,8 @@ async def fielddata( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -576,6 +691,9 @@ async def fielddata( async def health( self, *, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -652,6 +770,14 @@ async def health( ``_ + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -661,13 +787,20 @@ async def health( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param ts: If true, returns `HH:MM:SS` and Unix epoch timestamps. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] = {} __path = "/_cat/health" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -1092,7 +1225,14 @@ async def indices( :param index: Comma-separated list of data streams, indices, and aliases used to limit the request. Supports wildcards (`*`). To target all data streams and indices, omit this parameter or use `*` or `_all`. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param expand_wildcards: The type of index that wildcard patterns can match. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. @@ -1109,7 +1249,12 @@ async def indices( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -1166,6 +1311,9 @@ async def indices( async def master( self, *, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -1181,6 +1329,9 @@ async def master( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -1193,6 +1344,14 @@ async def master( ``_ + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -1207,11 +1366,19 @@ async def master( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] = {} __path = "/_cat/master" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -1232,6 +1399,8 @@ async def master( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -1374,8 +1543,15 @@ async def ml_data_frame_analytics( :param id: The ID of the data frame analytics to fetch :param allow_no_match: Whether to ignore if a wildcard expression matches no - configs. (This includes `_all` string or when no configs have been specified) - :param bytes: The unit in which to display byte values + configs. (This includes `_all` string or when no configs have been specified.) + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: Comma-separated list of column names to display. @@ -1383,7 +1559,12 @@ async def ml_data_frame_analytics( be combined with any other query string option. :param s: Comma-separated list of column names or column aliases used to sort the response. - :param time: Unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -1434,6 +1615,9 @@ async def ml_datafeeds( *, datafeed_id: t.Optional[str] = None, allow_no_match: t.Optional[bool] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -1549,6 +1733,14 @@ async def ml_datafeeds( array when there are no matches and the subset of results when there are partial matches. If `false`, the API returns a 404 status code when there are no matches or only partial matches. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: Comma-separated list of column names to display. @@ -1556,7 +1748,12 @@ async def ml_datafeeds( be combined with any other query string option. :param s: Comma-separated list of column names or column aliases used to sort the response. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -1569,6 +1766,8 @@ async def ml_datafeeds( __query: t.Dict[str, t.Any] = {} if allow_no_match is not None: __query["allow_no_match"] = allow_no_match + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -1914,7 +2113,14 @@ async def ml_jobs( array when there are no matches and the subset of results when there are partial matches. If `false`, the API returns a 404 status code when there are no matches or only partial matches. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: Comma-separated list of column names to display. @@ -1922,7 +2128,12 @@ async def ml_jobs( be combined with any other query string option. :param s: Comma-separated list of column names or column aliases used to sort the response. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -2099,7 +2310,14 @@ async def ml_trained_models( when there are no matches and the subset of results when there are partial matches. If `false`, the API returns a 404 status code when there are no matches or only partial matches. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param from_: Skips the specified number of transforms. @@ -2109,7 +2327,12 @@ async def ml_trained_models( :param s: A comma-separated list of column names or aliases used to sort the response. :param size: The maximum number of transforms to display. - :param time: Unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -2162,6 +2385,9 @@ async def ml_trained_models( async def nodeattrs( self, *, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -2189,6 +2415,9 @@ async def nodeattrs( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -2201,6 +2430,14 @@ async def nodeattrs( ``_ + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -2215,11 +2452,19 @@ async def nodeattrs( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] = {} __path = "/_cat/nodeattrs" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -2240,6 +2485,8 @@ async def nodeattrs( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -2262,7 +2509,7 @@ async def nodes( error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, - full_id: t.Optional[t.Union[bool, str]] = None, + full_id: t.Optional[bool] = None, h: t.Optional[ t.Union[ t.Sequence[ @@ -2478,7 +2725,14 @@ async def nodes( ``_ - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param full_id: If `true`, return the full node ID. If `false`, return the shortened @@ -2493,7 +2747,12 @@ async def nodes( :param s: A comma-separated list of column names or aliases that determines the sort order. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] = {} @@ -2541,6 +2800,9 @@ async def nodes( async def pending_tasks( self, *, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -2578,6 +2840,14 @@ async def pending_tasks( ``_ + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -2592,12 +2862,19 @@ async def pending_tasks( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: Unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] = {} __path = "/_cat/pending_tasks" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -2636,6 +2913,9 @@ async def pending_tasks( async def plugins( self, *, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -2659,6 +2939,9 @@ async def plugins( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -2671,6 +2954,14 @@ async def plugins( ``_ + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -2686,11 +2977,19 @@ async def plugins( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] = {} __path = "/_cat/plugins" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -2713,6 +3012,8 @@ async def plugins( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -2831,7 +3132,14 @@ async def recovery( to limit the request. Supports wildcards (`*`). To target all data streams and indices, omit this parameter or use `*` or `_all`. :param active_only: If `true`, the response only includes ongoing shard recoveries. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param detailed: If `true`, the response includes detailed information about shard recoveries. :param format: Specifies the format to return the columnar data in, can be set @@ -2843,7 +3151,12 @@ async def recovery( :param s: A comma-separated list of column names or aliases that determines the sort order. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -2894,6 +3207,9 @@ async def recovery( async def repositories( self, *, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -2904,6 +3220,9 @@ async def repositories( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -2916,6 +3235,14 @@ async def repositories( ``_ + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: List of columns to appear in the response. Supports simple wildcards. @@ -2929,11 +3256,19 @@ async def repositories( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] = {} __path = "/_cat/repositories" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -2954,6 +3289,8 @@ async def repositories( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -3029,6 +3366,9 @@ async def segments( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -3045,7 +3385,14 @@ async def segments( :param index: A comma-separated list of data streams, indices, and aliases used to limit the request. Supports wildcards (`*`). To target all data streams and indices, omit this parameter or use `*` or `_all`. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -3060,6 +3407,12 @@ async def segments( :param s: A comma-separated list of column names or aliases that determines the sort order. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -3092,6 +3445,8 @@ async def segments( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -3295,7 +3650,14 @@ async def shards( :param index: A comma-separated list of data streams, indices, and aliases used to limit the request. Supports wildcards (`*`). To target all data streams and indices, omit this parameter or use `*` or `_all`. - :param bytes: The unit used to display byte values. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: List of columns to appear in the response. Supports simple wildcards. @@ -3305,7 +3667,12 @@ async def shards( :param s: A comma-separated list of column names or aliases that determines the sort order. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -3355,6 +3722,9 @@ async def snapshots( self, *, repository: t.Optional[t.Union[str, t.Sequence[str]]] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -3425,6 +3795,14 @@ async def snapshots( :param repository: A comma-separated list of snapshot repositories used to limit the request. Accepts wildcard expressions. `_all` returns all repositories. If any repository fails during the request, Elasticsearch returns an error. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -3437,7 +3815,12 @@ async def snapshots( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: Unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -3448,6 +3831,8 @@ async def snapshots( __path_parts = {} __path = "/_cat/snapshots" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -3488,6 +3873,9 @@ async def tasks( self, *, actions: t.Optional[t.Sequence[str]] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, detailed: t.Optional[bool] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, @@ -3562,6 +3950,14 @@ async def tasks( ``_ :param actions: The task action names, which are used to limit the response. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param detailed: If `true`, the response includes detailed information about shard recoveries. :param format: Specifies the format to return the columnar data in, can be set @@ -3576,7 +3972,12 @@ async def tasks( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: Unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param timeout: Period to wait for a response. If no response is received before the timeout expires, the request fails and returns an error. :param v: When set to `true` will enable verbose output. @@ -3588,6 +3989,8 @@ async def tasks( __query: t.Dict[str, t.Any] = {} if actions is not None: __query["actions"] = actions + if bytes is not None: + __query["bytes"] = bytes if detailed is not None: __query["detailed"] = detailed if error_trace is not None: @@ -3633,6 +4036,9 @@ async def templates( self, *, name: t.Optional[str] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -3660,6 +4066,9 @@ async def templates( master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, pretty: t.Optional[bool] = None, s: t.Optional[t.Union[str, t.Sequence[str]]] = None, + time: t.Optional[ + t.Union[str, t.Literal["d", "h", "m", "micros", "ms", "nanos", "s"]] + ] = None, v: t.Optional[bool] = None, ) -> t.Union[ObjectApiResponse[t.Any], TextApiResponse]: """ @@ -3675,6 +4084,14 @@ async def templates( :param name: The name of the template to return. Accepts wildcard expressions. If omitted, all templates are returned. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: A comma-separated list of columns names to display. It supports simple @@ -3689,6 +4106,12 @@ async def templates( :param s: List of columns that determine how the table should be sorted. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -3699,6 +4122,8 @@ async def templates( __path_parts = {} __path = "/_cat/templates" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -3719,6 +4144,8 @@ async def templates( __query["pretty"] = pretty if s is not None: __query["s"] = s + if time is not None: + __query["time"] = time if v is not None: __query["v"] = v __headers = {"accept": "text/plain,application/json"} @@ -3736,6 +4163,9 @@ async def thread_pool( self, *, thread_pool_patterns: t.Optional[t.Union[str, t.Sequence[str]]] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -3819,6 +4249,14 @@ async def thread_pool( :param thread_pool_patterns: A comma-separated list of thread pool names used to limit the request. Accepts wildcard expressions. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param h: List of columns to appear in the response. Supports simple wildcards. @@ -3832,7 +4270,12 @@ async def thread_pool( :param s: A comma-separated list of column names or aliases that determines the sort order. Sorting defaults to ascending and can be changed by setting `:asc` or `:desc` as a suffix to the column name. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -3843,6 +4286,8 @@ async def thread_pool( __path_parts = {} __path = "/_cat/thread_pool" __query: t.Dict[str, t.Any] = {} + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: @@ -3885,6 +4330,9 @@ async def transforms( *, transform_id: t.Optional[str] = None, allow_no_match: t.Optional[bool] = None, + bytes: t.Optional[ + t.Union[str, t.Literal["b", "gb", "kb", "mb", "pb", "tb"]] + ] = None, error_trace: t.Optional[bool] = None, filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None, format: t.Optional[str] = None, @@ -4084,6 +4532,14 @@ async def transforms( array when there are no matches and the subset of results when there are partial matches. If `false`, the request returns a 404 status code when there are no matches or only partial matches. + :param bytes: Sets the units for columns that contain a byte-size value. Note + that byte-size value units work in terms of powers of 1024. For instance + `1kb` means 1024 bytes, not 1000 bytes. If omitted, byte-size values are + rendered with a suffix such as `kb`, `mb`, or `gb`, chosen such that the + numeric value of the column is as small as possible whilst still being at + least `1.0`. If given, byte-size values are rendered as an integer with no + suffix, representing the value of the column in the chosen unit. Values that + are not an exact multiple of the chosen unit are rounded down. :param format: Specifies the format to return the columnar data in, can be set to `text`, `json`, `cbor`, `yaml`, or `smile`. :param from_: Skips the specified number of transforms. @@ -4093,7 +4549,12 @@ async def transforms( :param s: Comma-separated list of column names or column aliases used to sort the response. :param size: The maximum number of transforms to obtain. - :param time: The unit used to display time values. + :param time: Sets the units for columns that contain a time duration. If omitted, + time duration values are rendered with a suffix such as `ms`, `s`, `m` or + `h`, chosen such that the numeric value of the column is as small as possible + whilst still being at least `1.0`. If given, time duration values are rendered + as an integer with no suffix. Values that are not an exact multiple of the + chosen unit are rounded down. :param v: When set to `true` will enable verbose output. """ __path_parts: t.Dict[str, str] @@ -4106,6 +4567,8 @@ async def transforms( __query: t.Dict[str, t.Any] = {} if allow_no_match is not None: __query["allow_no_match"] = allow_no_match + if bytes is not None: + __query["bytes"] = bytes if error_trace is not None: __query["error_trace"] = error_trace if filter_path is not None: diff --git a/elasticsearch/_async/client/connector.py b/elasticsearch/_async/client/connector.py index 6c4bca043..70e468db7 100644 --- a/elasticsearch/_async/client/connector.py +++ b/elasticsearch/_async/client/connector.py @@ -103,7 +103,7 @@ async def delete( :param connector_id: The unique identifier of the connector to be deleted :param delete_sync_jobs: A flag indicating if associated sync jobs should be - also removed. Defaults to false. + also removed. :param hard: A flag indicating if the connector should be hard deleted. """ if connector_id in SKIP_IN_PATH: @@ -360,7 +360,7 @@ async def list( :param connector_name: A comma-separated list of connector names to fetch connector documents for - :param from_: Starting offset (default: 0) + :param from_: Starting offset :param include_deleted: A flag to indicate if the desired connector should be fetched, even if it was soft-deleted. :param index_name: A comma-separated list of connector index names to fetch connector @@ -955,7 +955,7 @@ async def sync_job_list( ``_ :param connector_id: A connector id to fetch connector sync jobs for - :param from_: Starting offset (default: 0) + :param from_: Starting offset :param job_type: A comma-separated list of job types to fetch the sync jobs for :param size: Specifies a max number of results to get :param status: A sync job status to fetch connector sync jobs for diff --git a/elasticsearch/_async/client/eql.py b/elasticsearch/_async/client/eql.py index 98ee4f4e8..66e70037c 100644 --- a/elasticsearch/_async/client/eql.py +++ b/elasticsearch/_async/client/eql.py @@ -229,6 +229,7 @@ async def search( keep_on_completion: t.Optional[bool] = None, max_samples_per_key: t.Optional[int] = None, pretty: t.Optional[bool] = None, + project_routing: t.Optional[str] = None, result_position: t.Optional[t.Union[str, t.Literal["head", "tail"]]] = None, runtime_mappings: t.Optional[t.Mapping[str, t.Mapping[str, t.Any]]] = None, size: t.Optional[int] = None, @@ -285,6 +286,10 @@ async def search( `size` parameter to get a smaller or larger set of samples. To retrieve more than one sample per set of join keys, use the `max_samples_per_key` parameter. Pipes are not supported for sample queries. + :param project_routing: Specifies a subset of projects to target for the search + using project metadata tags in a subset of Lucene query syntax. Allowed Lucene + queries: the _alias tag and a single value (possibly wildcarded). Examples: + _alias:my-project _alias:_origin _alias:*pr* Supported in serverless only. :param result_position: :param runtime_mappings: :param size: For basic queries, the maximum number of matching events to return. @@ -318,6 +323,8 @@ async def search( __query["ignore_unavailable"] = ignore_unavailable if pretty is not None: __query["pretty"] = pretty + if project_routing is not None: + __query["project_routing"] = project_routing if not __body: if query is not None: __body["query"] = query diff --git a/elasticsearch/_async/client/esql.py b/elasticsearch/_async/client/esql.py index 38e642779..79f28a8a3 100644 --- a/elasticsearch/_async/client/esql.py +++ b/elasticsearch/_async/client/esql.py @@ -40,6 +40,7 @@ class EsqlClient(NamespacedClient): "columnar", "filter", "include_ccs_metadata", + "include_execution_metadata", "keep_alive", "keep_on_completion", "locale", @@ -71,6 +72,7 @@ async def async_query( ] = None, human: t.Optional[bool] = None, include_ccs_metadata: t.Optional[bool] = None, + include_execution_metadata: t.Optional[bool] = None, keep_alive: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None, keep_on_completion: t.Optional[bool] = None, locale: t.Optional[str] = None, @@ -120,7 +122,11 @@ async def async_query( be returned if the async query doesn't finish within the timeout. The query ID and running status are available in the `X-Elasticsearch-Async-Id` and `X-Elasticsearch-Async-Is-Running` HTTP headers of the response, respectively. - :param include_ccs_metadata: When set to `true` and performing a cross-cluster + :param include_ccs_metadata: When set to `true` and performing a cross-cluster/cross-project + query, the response will include an extra `_clusters` object with information + about the clusters that participated in the search along with info such as + shards count. + :param include_execution_metadata: When set to `true` and performing a cross-cluster/cross-project query, the response will include an extra `_clusters` object with information about the clusters that participated in the search along with info such as shards count. @@ -180,6 +186,8 @@ async def async_query( __body["filter"] = filter if include_ccs_metadata is not None: __body["include_ccs_metadata"] = include_ccs_metadata + if include_execution_metadata is not None: + __body["include_execution_metadata"] = include_execution_metadata if keep_alive is not None: __body["keep_alive"] = keep_alive if keep_on_completion is not None: @@ -486,6 +494,7 @@ async def list_queries( "columnar", "filter", "include_ccs_metadata", + "include_execution_metadata", "locale", "params", "profile", @@ -514,8 +523,16 @@ async def query( ] = None, human: t.Optional[bool] = None, include_ccs_metadata: t.Optional[bool] = None, + include_execution_metadata: t.Optional[bool] = None, locale: t.Optional[str] = None, - params: t.Optional[t.Sequence[t.Union[None, bool, float, int, str]]] = None, + params: t.Optional[ + t.Sequence[ + t.Union[ + t.Sequence[t.Union[None, bool, float, int, str]], + t.Union[None, bool, float, int, str], + ] + ] + ] = None, pretty: t.Optional[bool] = None, profile: t.Optional[bool] = None, tables: t.Optional[ @@ -554,7 +571,11 @@ async def query( :param format: A short version of the Accept header, e.g. json, yaml. `csv`, `tsv`, and `txt` formats will return results in a tabular format, excluding other metadata fields from the response. - :param include_ccs_metadata: When set to `true` and performing a cross-cluster + :param include_ccs_metadata: When set to `true` and performing a cross-cluster/cross-project + query, the response will include an extra `_clusters` object with information + about the clusters that participated in the search along with info such as + shards count. + :param include_execution_metadata: When set to `true` and performing a cross-cluster/cross-project query, the response will include an extra `_clusters` object with information about the clusters that participated in the search along with info such as shards count. @@ -600,6 +621,8 @@ async def query( __body["filter"] = filter if include_ccs_metadata is not None: __body["include_ccs_metadata"] = include_ccs_metadata + if include_execution_metadata is not None: + __body["include_execution_metadata"] = include_execution_metadata if locale is not None: __body["locale"] = locale if params is not None: diff --git a/elasticsearch/_async/client/fleet.py b/elasticsearch/_async/client/fleet.py index f1ea60007..de2be54cb 100644 --- a/elasticsearch/_async/client/fleet.py +++ b/elasticsearch/_async/client/fleet.py @@ -642,11 +642,7 @@ async def search( __body["track_total_hits"] = track_total_hits if version is not None: __body["version"] = version - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", __path, diff --git a/elasticsearch/_async/client/graph.py b/elasticsearch/_async/client/graph.py index a8c35dbfe..d239cf4ae 100644 --- a/elasticsearch/_async/client/graph.py +++ b/elasticsearch/_async/client/graph.py @@ -97,11 +97,7 @@ async def explore( __body["query"] = query if vertices is not None: __body["vertices"] = vertices - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", __path, diff --git a/elasticsearch/_async/client/ilm.py b/elasticsearch/_async/client/ilm.py index c15aaa807..acc7f14b8 100644 --- a/elasticsearch/_async/client/ilm.py +++ b/elasticsearch/_async/client/ilm.py @@ -383,11 +383,7 @@ async def move_to_step( __body["current_step"] = current_step if next_step is not None: __body["next_step"] = next_step - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", __path, @@ -453,11 +449,7 @@ async def put_lifecycle( if not __body: if policy is not None: __body["policy"] = policy - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "PUT", __path, diff --git a/elasticsearch/_async/client/indices.py b/elasticsearch/_async/client/indices.py index 7beaa591a..082b860c3 100644 --- a/elasticsearch/_async/client/indices.py +++ b/elasticsearch/_async/client/indices.py @@ -232,11 +232,7 @@ async def analyze( __body["text"] = text if tokenizer is not None: __body["tokenizer"] = tokenizer - if not __body: - __body = None # type: ignore[assignment] - __headers = {"accept": "application/json"} - if __body is not None: - __headers["content-type"] = "application/json" + __headers = {"accept": "application/json", "content-type": "application/json"} return await self.perform_request( # type: ignore[return-value] "POST", __path, @@ -812,11 +808,7 @@ async def create_from( raise ValueError("Empty value passed for parameter 'source'") if dest in SKIP_IN_PATH: raise ValueError("Empty value passed for parameter 'dest'") - if create_from is None and body is None: - raise ValueError( - "Empty value passed for parameters 'create_from' and 'body', one of them should be set." - ) - elif create_from is not None and body is not None: + if create_from is not None and body is not None: raise ValueError("Cannot set both 'create_from' and 'body'") __path_parts: t.Dict[str, str] = { "source": _quote(source), @@ -833,7 +825,11 @@ async def create_from( if pretty is not None: __query["pretty"] = pretty __body = create_from if create_from is not None else body - __headers = {"accept": "application/json", "content-type": "application/json"} + if not __body: + __body = None + __headers = {"accept": "application/json"} + if __body is not None: + __headers["content-type"] = "application/json" return await self.perform_request( # type: ignore[return-value] "PUT", __path, @@ -1393,6 +1389,7 @@ async def disk_usage(