Skip to content

Commit a463ea6

Browse files
committed
create template files
1 parent 5ea61c2 commit a463ea6

12 files changed

Lines changed: 384 additions & 683 deletions

File tree

packages/bigframes/bigframes/bigquery/_googlesql.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def apply_googlesql_scalar_op(
5858
if isinstance(arg, bigframes.core.col.Expression):
5959
block, col_id = block.project_expr(bigframes.core.col._as_bf_expr(arg))
6060
processed_args.append(series.Series(block.select_column(col_id)))
61-
elif arg is sentinels.DEFAULT:
61+
elif arg is sentinels.Sentinel.ARGUMENT_DEFAULT:
6262
processed_args.append(bigframes.core.col.Expression(ex.OmittedArg()))
6363
else:
6464
processed_args.append(arg)
@@ -73,7 +73,7 @@ def apply_googlesql_scalar_op(
7373
for arg in args:
7474
if isinstance(arg, bigframes.core.col.Expression):
7575
expr_args.append(bigframes.core.col._as_bf_expr(arg))
76-
elif arg is sentinels.DEFAULT:
76+
elif arg is sentinels.Sentinel.ARGUMENT_DEFAULT:
7777
expr_args.append(ex.OmittedArg())
7878
else:
7979
expr_args.append(ex.const(arg))

packages/bigframes/bigframes/bigquery/_operations/aead.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
1514
#
1615
# DO NOT MODIFY THIS FILE DIRECTLY.
1716
# This file was generated from: scripts/data/sql-functions/aead.yaml
@@ -20,7 +19,7 @@
2019
from __future__ import annotations
2120

2221
import datetime
23-
from typing import Any, Optional, TypeVar, Union
22+
from typing import Any, Literal, Optional, TypeVar, Union
2423

2524
import bigframes.bigquery._googlesql
2625
import bigframes.core.col
@@ -51,9 +50,9 @@
5150

5251

5352
def decrypt_bytes(
54-
keyset: Union[T, bigframes.core.col.Expression, Union[bytes, dict]],
55-
ciphertext: Union[T, bigframes.core.col.Expression, bytes],
56-
additional_data: Union[T, bigframes.core.col.Expression, bytes],
53+
keyset: Union[T, bigframes.core.col.Expression, Union[Literal[sentinels.Sentinel.ARGUMENT_DEFAULT], bytes, dict]],
54+
ciphertext: Union[T, bigframes.core.col.Expression, Union[Literal[sentinels.Sentinel.ARGUMENT_DEFAULT], bytes]],
55+
additional_data: Union[T, bigframes.core.col.Expression, Union[Literal[sentinels.Sentinel.ARGUMENT_DEFAULT], bytes]],
5756
) -> T:
5857
"""Uses the matching key from keyset to decrypt ciphertext and verifies the integrity of the data using additional_data. Returns an error if decryption or verification fails."""
5958
return bigframes.bigquery._googlesql.apply_googlesql_scalar_op(
@@ -65,9 +64,9 @@ def decrypt_bytes(
6564

6665

6766
def decrypt_string(
68-
keyset: Union[T, bigframes.core.col.Expression, Union[bytes, dict]],
69-
ciphertext: Union[T, bigframes.core.col.Expression, bytes],
70-
additional_data: Union[T, bigframes.core.col.Expression, str],
67+
keyset: Union[T, bigframes.core.col.Expression, Union[Literal[sentinels.Sentinel.ARGUMENT_DEFAULT], bytes, dict]],
68+
ciphertext: Union[T, bigframes.core.col.Expression, Union[Literal[sentinels.Sentinel.ARGUMENT_DEFAULT], bytes]],
69+
additional_data: Union[T, bigframes.core.col.Expression, Union[Literal[sentinels.Sentinel.ARGUMENT_DEFAULT], str]],
7170
) -> T:
7271
"""Like AEAD.DECRYPT_BYTES, but where additional_data is of type STRING."""
7372
return bigframes.bigquery._googlesql.apply_googlesql_scalar_op(
@@ -79,9 +78,9 @@ def decrypt_string(
7978

8079

8180
def encrypt(
82-
keyset: Union[T, bigframes.core.col.Expression, Union[bytes, dict]],
83-
plaintext: Union[T, bigframes.core.col.Expression, Union[bytes, str]],
84-
additional_data: Union[T, bigframes.core.col.Expression, Union[bytes, str]],
81+
keyset: Union[T, bigframes.core.col.Expression, Union[Literal[sentinels.Sentinel.ARGUMENT_DEFAULT], bytes, dict]],
82+
plaintext: Union[T, bigframes.core.col.Expression, Union[Literal[sentinels.Sentinel.ARGUMENT_DEFAULT], bytes, str]],
83+
additional_data: Union[T, bigframes.core.col.Expression, Union[Literal[sentinels.Sentinel.ARGUMENT_DEFAULT], bytes, str]],
8584
) -> T:
8685
"""Encrypts plaintext using the primary cryptographic key in keyset. The algorithm of the primary key must be AEAD_AES_GCM_256. Binds the ciphertext to the context defined by additional_data. Returns NULL if any input is NULL."""
8786
return bigframes.bigquery._googlesql.apply_googlesql_scalar_op(

packages/bigframes/bigframes/core/sentinels.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,16 @@
1616

1717
from __future__ import annotations
1818

19-
from enum import Enum
19+
import enum
2020

2121

22-
class Default(Enum):
23-
"""Default values used throughout BigFrames.
22+
class Sentinel(enum.Enum):
23+
"""Default values used throughout BigFrames."""
24+
25+
"""Default value for an optional argument.
2426
2527
When a parameter is set to this, that parameter is explicitly omitted
2628
from the SQL text. This allows for NULL (None in Python) to be explicitly
2729
passed in to optional parameters.
2830
"""
29-
30-
token = 0
31-
32-
33-
DEFAULT = Default.token
31+
ARGUMENT_DEFAULT = enum.auto()

packages/bigframes/scripts/generate_bigframes_bigquery.py

Lines changed: 11 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
OUTPUT_DIR = pathlib.Path("bigframes/bigquery/_operations")
3636
# Directory where the generated test files will be placed
3737
TEST_OUTPUT_DIR = pathlib.Path("tests/unit/bigquery/_operations")
38+
# Directory containing the Jinja2 templates
39+
TEMPLATE_DIR = pathlib.Path("scripts/templates")
3840

3941
RUFF_ARGS = [
4042
"ruff",
@@ -46,97 +48,6 @@
4648
"--line-length=88",
4749
]
4850

49-
LICENSE_HEADER = """# Copyright 2026 Google LLC
50-
#
51-
# Licensed under the Apache License, Version 2.0 (the "License");
52-
# you may not use this file except in compliance with the License.
53-
# You may obtain a copy of the License at
54-
#
55-
# http://www.apache.org/licenses/LICENSE-2.0
56-
#
57-
# Unless required by applicable law or agreed to in writing, software
58-
# distributed under the License is distributed on an "AS IS" BASIS,
59-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
60-
# See the License for the specific language governing permissions and
61-
# limitations under the License.
62-
"""
63-
64-
TEMPLATE = """{{ license_header }}
65-
#
66-
# DO NOT MODIFY THIS FILE DIRECTLY.
67-
# This file was generated from: {{ yaml_path }}
68-
# by the script: {{ script_path }}
69-
70-
from __future__ import annotations
71-
72-
import datetime
73-
from typing import Any, Optional, TypeVar, Union
74-
75-
from bigframes import dtypes
76-
import bigframes.bigquery._googlesql
77-
import bigframes.core.col
78-
import bigframes.core.expression as ex
79-
import bigframes.core.sentinels as sentinels
80-
from bigframes.operations import googlesql
81-
import bigframes.operations as ops
82-
import bigframes.series as series
83-
84-
T = TypeVar("T", series.Series, bigframes.core.col.Expression)
85-
86-
{% for op in ops %}
87-
{{ op.internal_name }} = googlesql.GoogleSqlScalarOp(
88-
"{{ op.sql_name }}",
89-
args=({{ op.arg_specs }}),
90-
signature={{ op.signature }},
91-
)
92-
{% endfor %}
93-
{% for func in functions %}
94-
95-
96-
def {{ func.name }}(
97-
{% for arg in func.args %}
98-
{{ arg.name }}: Union[T, bigframes.core.col.Expression, {{ arg.type_hint }}]{% if arg.default %} = {{ arg.default }}{% endif %},
99-
{% endfor %}
100-
) -> T:
101-
\"\"\"{{ func.description }}\"\"\"
102-
return bigframes.bigquery._googlesql.apply_googlesql_scalar_op(
103-
{{ func.op_name }},
104-
{% for arg in func.args %}
105-
{{ arg.name }},
106-
{% endfor %}
107-
) # type: ignore
108-
{% endfor %}
109-
"""
110-
111-
TEST_TEMPLATE = r"""{{ license_header }}
112-
#
113-
# DO NOT MODIFY THIS FILE DIRECTLY.
114-
# This file was generated from: {{ yaml_path }}
115-
# by the script: {{ script_path }}
116-
117-
from typing import cast
118-
119-
import pytest
120-
121-
import bigframes.pandas as bpd
122-
import {{ import_path }} as {{ short_name }}
123-
124-
pytest.importorskip("pytest_snapshot")
125-
126-
127-
{% for func in functions %}
128-
def test_{{ func.name }}(scalar_types_df: bpd.DataFrame, snapshot):
129-
result = {{ short_name }}.{{ func.name }}(
130-
{% for arg in func.test_args %}
131-
cast(bpd.Series, scalar_types_df["{{ arg.col_name }}"]),
132-
{% endfor %}
133-
).to_frame()
134-
snapshot.assert_match(result.sql.rstrip() + "\n", "out.sql")
135-
136-
137-
{% endfor %}
138-
"""
139-
14051
DTYPE_MAP = {
14152
"binary": "dtypes.BYTES_DTYPE",
14253
"string": "dtypes.STRING_DTYPE",
@@ -191,9 +102,13 @@ def to_snake_case(name):
191102

192103

193104
def main():
194-
env = jinja2.Environment(trim_blocks=True, lstrip_blocks=True)
195-
template = env.from_string(TEMPLATE)
196-
test_template = env.from_string(TEST_TEMPLATE)
105+
env = jinja2.Environment(
106+
loader=jinja2.FileSystemLoader(TEMPLATE_DIR),
107+
trim_blocks=True,
108+
lstrip_blocks=True,
109+
)
110+
template = env.get_template("operation.py.j2")
111+
test_template = env.get_template("test_operation.py.j2")
197112

198113
for yaml_file in DATA_DIR.glob("**/*.yaml"):
199114
print(f"Processing {yaml_file}...")
@@ -266,13 +181,13 @@ def main():
266181
func_args = []
267182
for name in arg_order:
268183
arg_info = args_by_name[name]
269-
types = [PY_TYPE_MAP.get(t, "Any") for t in arg_info["types"]]
184+
types = [PY_TYPE_MAP.get(t, "Any") for t in arg_info["types"]] + ["Literal[sentinels.Sentinel.ARGUMENT_DEFAULT]"]
270185
type_hint = (
271186
"Union[" + ", ".join(sorted(set(types))) + "]"
272187
if len(types) > 1
273188
else types[0]
274189
)
275-
default = "sentinels.DEFAULT" if arg_info["optional"] else ""
190+
default = "sentinels.Sentinel.ARGUMENT_DEFAULT" if arg_info["optional"] else ""
276191
func_args.append(
277192
{
278193
"name": name,
@@ -308,7 +223,6 @@ def main():
308223
# Render and write
309224
output_file.parent.mkdir(parents=True, exist_ok=True)
310225
content = template.render(
311-
license_header=LICENSE_HEADER,
312226
yaml_path=str(yaml_file),
313227
script_path="scripts/generate_bigframes_bigquery.py",
314228
ops=ops_list,
@@ -334,7 +248,6 @@ def main():
334248

335249
test_output_file.parent.mkdir(parents=True, exist_ok=True)
336250
test_content = test_template.render(
337-
license_header=LICENSE_HEADER,
338251
yaml_path=str(yaml_file),
339252
script_path="scripts/generate_bigframes_bigquery.py",
340253
import_path=import_path,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{% include 'license.py.j2' %}
2+
3+
#
4+
# DO NOT MODIFY THIS FILE DIRECTLY.
5+
# This file was generated from: {{ yaml_path }}
6+
# by the script: {{ script_path }}
7+
8+
from __future__ import annotations
9+
10+
import datetime
11+
from typing import Any, Literal, Optional, TypeVar, Union
12+
13+
from bigframes import dtypes
14+
import bigframes.bigquery._googlesql
15+
import bigframes.core.col
16+
import bigframes.core.expression as ex
17+
import bigframes.core.sentinels as sentinels
18+
from bigframes.operations import googlesql
19+
import bigframes.operations as ops
20+
import bigframes.series as series
21+
22+
T = TypeVar("T", series.Series, bigframes.core.col.Expression)
23+
24+
{% for op in ops %}
25+
{{ op.internal_name }} = googlesql.GoogleSqlScalarOp(
26+
"{{ op.sql_name }}",
27+
args=({{ op.arg_specs }}),
28+
signature={{ op.signature }},
29+
)
30+
{% endfor %}
31+
{% for func in functions %}
32+
33+
34+
def {{ func.name }}(
35+
{% for arg in func.args %}
36+
{{ arg.name }}: Union[T, bigframes.core.col.Expression, {{ arg.type_hint }}]{% if arg.default %} = {{ arg.default }}{% endif %},
37+
{% endfor %}
38+
) -> T:
39+
"""{{ func.description }}"""
40+
return bigframes.bigquery._googlesql.apply_googlesql_scalar_op(
41+
{{ func.op_name }},
42+
{% for arg in func.args %}
43+
{{ arg.name }},
44+
{% endfor %}
45+
) # type: ignore
46+
{% endfor %}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{% include 'license.py.j2' %}
2+
3+
#
4+
# DO NOT MODIFY THIS FILE DIRECTLY.
5+
# This file was generated from: {{ yaml_path }}
6+
# by the script: {{ script_path }}
7+
8+
from typing import cast
9+
10+
import pytest
11+
12+
import bigframes.pandas as bpd
13+
import {{ import_path }} as {{ short_name }}
14+
15+
pytest.importorskip("pytest_snapshot")
16+
17+
18+
{% for func in functions %}
19+
def test_{{ func.name }}(scalar_types_df: bpd.DataFrame, snapshot):
20+
result = {{ short_name }}.{{ func.name }}(
21+
{% for arg in func.test_args %}
22+
cast(bpd.Series, scalar_types_df["{{ arg.col_name }}"]),
23+
{% endfor %}
24+
).to_frame()
25+
snapshot.assert_match(result.sql.rstrip() + "\n", "out.sql")
26+
27+
28+
{% endfor %}

0 commit comments

Comments
 (0)