Coverage for python/lsst/daf/butler/queries/convert_args.py: 16%
46 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-08 02:51 -0700
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-08 02:51 -0700
1# This file is part of daf_butler.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This software is dual licensed under the GNU General Public License and also
10# under a 3-clause BSD license. Recipients may choose which of these licenses
11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12# respectively. If you choose the GPL option then the following text applies
13# (but note that there is still no warranty even if you opt for BSD instead):
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU General Public License as published by
17# the Free Software Foundation, either version 3 of the License, or
18# (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23# GNU General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program. If not, see <http://www.gnu.org/licenses/>.
28from __future__ import annotations
30__all__ = (
31 "convert_where_args",
32 "convert_order_by_args",
33)
35from collections.abc import Mapping, Set
36from typing import Any
38from ..dimensions import DataCoordinate, DataId, DimensionGroup
39from ._expression_strings import convert_expression_string_to_predicate
40from ._identifiers import IdentifierContext, interpret_identifier
41from .expression_factory import ExpressionFactory, ExpressionProxy
42from .tree import (
43 DimensionKeyReference,
44 OrderExpression,
45 Predicate,
46 Reversed,
47 make_column_literal,
48 validate_order_expression,
49)
52def convert_where_args(
53 dimensions: DimensionGroup,
54 datasets: Set[str],
55 *args: str | Predicate | DataId,
56 bind: Mapping[str, Any] | None = None,
57 **kwargs: Any,
58) -> Predicate:
59 """Convert ``where`` arguments to a sequence of column expressions.
61 Parameters
62 ----------
63 dimensions : `DimensionGroup`
64 Dimensions already present in the query this filter is being applied
65 to. Returned predicates may reference dimensions outside this set.
66 datasets : `~collections.abc.Set` [ `str` ]
67 Dataset types already present in the query this filter is being applied
68 to. Returned predicates may reference datasets outside this set; this
69 may be an error at a higher level, but it is not necessarily checked
70 here.
71 *args : `str`, `Predicate`, `DataCoordinate`, or `~collections.abc.Mapping`
72 Expressions to convert into predicates.
73 bind : `~collections.abc.Mapping`, optional
74 Mapping from identifier to literal value used when parsing string
75 expressions.
76 **kwargs : `object`
77 Additional data ID key-value pairs.
79 Returns
80 -------
81 predicate : `Predicate`
82 Standardized predicate object.
84 Notes
85 -----
86 Data ID values are not checked for consistency; they are extracted from
87 args and then kwargs and combined, with later extractions taking
88 precedence.
89 """
90 context = IdentifierContext(dimensions, datasets, bind)
91 result = Predicate.from_bool(True)
92 data_id_dict: dict[str, Any] = {}
93 for arg in args:
94 match arg:
95 case str():
96 result = result.logical_and(
97 convert_expression_string_to_predicate(arg, context=context, universe=dimensions.universe)
98 )
99 case Predicate():
100 result = result.logical_and(arg)
101 case DataCoordinate():
102 data_id_dict.update(arg.mapping)
103 case _:
104 data_id_dict.update(arg)
105 data_id_dict.update(kwargs)
106 for k, v in data_id_dict.items():
107 result = result.logical_and(
108 Predicate.compare(
109 DimensionKeyReference.model_construct(dimension=dimensions.universe.dimensions[k]),
110 "==",
111 make_column_literal(v),
112 )
113 )
114 return result
117def convert_order_by_args(
118 dimensions: DimensionGroup, datasets: Set[str], *args: str | OrderExpression | ExpressionProxy
119) -> tuple[OrderExpression, ...]:
120 """Convert ``order_by`` arguments to a sequence of column expressions.
122 Parameters
123 ----------
124 dimensions : `DimensionGroup`
125 Dimensions already present in the query whose rows are being sorted.
126 Returned expressions may reference dimensions outside this set; this
127 may be an error at a higher level, but it is not necessarily checked
128 here.
129 datasets : `~collections.abc.Set` [ `str` ]
130 Dataset types already present in the query whose rows are being sorted.
131 Returned expressions may reference datasets outside this set; this may
132 be an error at a higher level, but it is not necessarily checked here.
133 *args : `OrderExpression`, `str`, or `ExpressionObject`
134 Expression or column names to sort by.
136 Returns
137 -------
138 expressions : `tuple` [ `OrderExpression`, ... ]
139 Standardized expression objects.
140 """
141 context = IdentifierContext(dimensions, datasets)
142 result: list[OrderExpression] = []
143 for arg in args:
144 match arg:
145 case str():
146 reverse = False
147 if arg.startswith("-"):
148 reverse = True
149 arg = arg[1:]
150 arg = interpret_identifier(context, arg)
151 if reverse:
152 arg = Reversed(operand=arg)
153 case ExpressionProxy():
154 arg = ExpressionFactory.unwrap(arg)
155 if not hasattr(arg, "expression_type"):
156 raise TypeError(f"Unrecognized order-by argument: {arg!r}.")
157 result.append(validate_order_expression(arg))
158 return tuple(result)