Skip to content

Commit e33a574

Browse files
committed
refactors the generator for clarity and to gen code per design
1 parent 2f10bf9 commit e33a574

File tree

3 files changed

+197
-0
lines changed

3 files changed

+197
-0
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2025 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
"""A utility module for handling name transformations."""
17+
18+
import re
19+
from typing import Dict
20+
21+
def to_snake_case(name: str) -> str:
22+
"""Converts a PascalCase name to snake_case."""
23+
return re.sub(r"(?<!^)(?=[A-Z])", "_", name).lower()
24+
25+
def generate_service_names(class_name: str) -> Dict[str, str]:
26+
"""
27+
Generates various name formats for a service based on its client class name.
28+
29+
Args:
30+
class_name: The PascalCase name of the service client class
31+
(e.g., 'DatasetServiceClient').
32+
33+
Returns:
34+
A dictionary containing different name variations.
35+
"""
36+
snake_case_name = to_snake_case(class_name)
37+
module_name = snake_case_name.replace("_client", "")
38+
service_name = module_name.replace("_service", "")
39+
40+
return {
41+
"service_name": service_name,
42+
"service_module_name": module_name,
43+
"service_client_class": class_name,
44+
"property_name": snake_case_name, # Direct use of snake_case_name
45+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# TODO: Add a header if needed.
2+
3+
# ======== 🦕 HERE THERE BE DINOSAURS 🦖 =========
4+
# This content is subject to significant change. Not for review yet.
5+
# Included as a proof of concept for context or testing ONLY.
6+
# ================================================
7+
8+
# Imports
9+
import os
10+
11+
from typing import (
12+
Dict,
13+
Optional,
14+
Sequence,
15+
Tuple,
16+
Union,
17+
)
18+
19+
20+
{% for imp in service_imports %}
21+
{{ imp }}
22+
{% endfor %}
23+
from google.cloud.bigquery_v2.services.centralized_service import _helpers
24+
25+
{% for imp in type_imports %}
26+
{{ imp }}
27+
{% endfor %}
28+
from google.cloud.bigquery_v2.types import dataset_reference
29+
30+
from google.api_core import client_options as client_options_lib
31+
from google.api_core import gapic_v1
32+
from google.api_core import retry as retries
33+
from google.auth import credentials as auth_credentials
34+
35+
# Create type aliases
36+
try:
37+
OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None]
38+
except AttributeError: # pragma: NO COVER
39+
OptionalRetry = Union[retries.Retry, object, None] # type: ignore
40+
41+
DatasetIdentifier = Union[str, dataset_reference.DatasetReference]
42+
43+
DEFAULT_RETRY: OptionalRetry = gapic_v1.method.DEFAULT
44+
DEFAULT_TIMEOUT: Union[float, object] = gapic_v1.method.DEFAULT
45+
DEFAULT_METADATA: Sequence[Tuple[str, Union[str, bytes]]] = ()
46+
47+
48+
class BigQueryClient:
49+
def __init__(self, credentials=None, client_options=None):
50+
self._clients = {}
51+
self._credentials = credentials
52+
self._client_options = client_options
53+
54+
# --- *METHOD SECTION ---
55+
{% for method in methods %}
56+
def {{ method.name }}(
57+
self,
58+
*,
59+
request: Optional["{{ method.service_module_name.replace('_service', '') }}.{{ method.name.replace('_', ' ').title().replace(' ', '') }}Request"] = None,
60+
retry: OptionalRetry = DEFAULT_RETRY,
61+
timeout: Union[float, object] = DEFAULT_TIMEOUT,
62+
metadata: Sequence[Tuple[str, Union[str, bytes]]] = DEFAULT_METADATA,
63+
) -> "{{ method.return_type }}":
64+
"""
65+
TODO: Docstring is purposefully blank. microgenerator will add automatically.
66+
"""
67+
68+
return self.{{ method.service_module_name }}_client.{{ method.name }}(
69+
request=request,
70+
retry=retry,
71+
timeout=timeout,
72+
metadata=metadata,
73+
)
74+
{% endfor %}
75+
76+
{#- *ServiceClient Properties Section: methods to get/set service clients -#}
77+
# --- *SERVICECLIENT PROPERTIES ---
78+
{% for service in services %}
79+
@property
80+
def {{ service.property_name }}(self):
81+
if "{{ service.service_name }}" not in self._clients:
82+
self._clients["{{ service.service_name }}"] = {{ service.service_module_name }}.{{ service.service_client_class }}(
83+
credentials=self._credentials, client_options=self._client_options
84+
)
85+
return self._clients["{{ service.service_name }}"]
86+
87+
@{{ service.property_name }}.setter
88+
def {{ service.property_name }}(self, value):
89+
if not isinstance(value, {{ service.service_module_name }}.{{ service.service_client_class }}):
90+
raise TypeError(
91+
"Expected an instance of {{ service.service_module_name }}.{{ service.service_client_class }}."
92+
)
93+
self._clients["{{ service.service_name }}"] = value
94+
{% endfor %}
95+
96+
{#- Helper Section: methods included from partial template -#}
97+
{%- include "partials/_client_helpers.j2" %}
98+
99+
100+
# ======== 🦕 HERE THERE WERE DINOSAURS 🦖 =========
101+
# The above content is subject to significant change. Not for review yet.
102+
# Included as a proof of concept for context or testing ONLY.
103+
# ================================================
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{#
2+
This is a partial template file intended to be included in other templates.
3+
It contains helper methods for the BigQueryClient class.
4+
#}
5+
6+
# --- HELPER METHODS ---
7+
def _parse_dataset_path(self, dataset_path: str) -> Tuple[Optional[str], str]:
8+
"""
9+
Helper to parse project_id and/or dataset_id from a string identifier.
10+
11+
Args:
12+
dataset_path: A string in the format 'project_id.dataset_id' or
13+
'dataset_id'.
14+
15+
Returns:
16+
A tuple of (project_id, dataset_id).
17+
"""
18+
if "." in dataset_path:
19+
# Use rsplit to handle legacy paths like `google.com:my-project.my_dataset`.
20+
project_id, dataset_id = dataset_path.rsplit(".", 1)
21+
return project_id, dataset_id
22+
return self.project, dataset_path
23+
24+
def _parse_dataset_id_to_dict(self, dataset_id: "DatasetIdentifier") -> dict:
25+
"""
26+
Helper to create a dictionary from a project_id and dataset_id to pass
27+
internally between helper functions.
28+
29+
Args:
30+
dataset_id: A string or DatasetReference.
31+
32+
Returns:
33+
A dict of {"project_id": project_id, "dataset_id": dataset_id_str }.
34+
"""
35+
if isinstance(dataset_id, str):
36+
project_id, dataset_id_str = self._parse_dataset_path(dataset_id)
37+
return {"project_id": project_id, "dataset_id": dataset_id_str}
38+
elif isinstance(dataset_id, dataset_reference.DatasetReference):
39+
return {
40+
"project_id": dataset_id.project_id,
41+
"dataset_id": dataset_id.dataset_id,
42+
}
43+
else:
44+
raise TypeError(f"Invalid type for dataset_id: {type(dataset_id)}")
45+
46+
def _parse_project_id_to_dict(self, project_id: Optional[str] = None) -> dict:
47+
"""Helper to create a request dictionary from a project_id."""
48+
final_project_id = project_id or self.project
49+
return {"project_id": final_project_id}

0 commit comments

Comments
 (0)