garf-executors 0.1.5__tar.gz → 0.1.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {garf_executors-0.1.5 → garf_executors-0.1.7}/PKG-INFO +1 -1
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors/__init__.py +1 -1
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors/api_executor.py +21 -15
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors/bq_executor.py +25 -16
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors/entrypoints/cli.py +10 -5
- garf_executors-0.1.7/garf_executors/entrypoints/grpc_server.py +68 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors/execution_context.py +38 -5
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors/fetchers.py +7 -3
- garf_executors-0.1.7/garf_executors/garf_pb2.py +45 -0
- garf_executors-0.1.7/garf_executors/garf_pb2_grpc.py +97 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors/sql_executor.py +21 -14
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors.egg-info/PKG-INFO +1 -1
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors.egg-info/SOURCES.txt +3 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/README.md +0 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors/config.py +0 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors/entrypoints/__init__.py +0 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors/entrypoints/server.py +0 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors/entrypoints/tracer.py +0 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors/entrypoints/utils.py +0 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors/exceptions.py +0 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors/executor.py +0 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors/telemetry.py +0 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors.egg-info/dependency_links.txt +0 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors.egg-info/entry_points.txt +0 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors.egg-info/requires.txt +0 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/garf_executors.egg-info/top_level.txt +0 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/pyproject.toml +0 -0
- {garf_executors-0.1.5 → garf_executors-0.1.7}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: garf-executors
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
4
4
|
Summary: Executes queries against API and writes data to local/remote storage.
|
|
5
5
|
Author-email: "Google Inc. (gTech gPS CSE team)" <no-reply@google.com>, Andrei Markin <andrey.markin.ppc@gmail.com>
|
|
6
6
|
License: Apache 2.0
|
|
@@ -20,7 +20,6 @@ GarfReport and saving it to local/remote storage.
|
|
|
20
20
|
|
|
21
21
|
from __future__ import annotations
|
|
22
22
|
|
|
23
|
-
import asyncio
|
|
24
23
|
import logging
|
|
25
24
|
|
|
26
25
|
from garf_core import report_fetcher
|
|
@@ -35,7 +34,7 @@ logger = logging.getLogger(__name__)
|
|
|
35
34
|
class ApiExecutionContext(execution_context.ExecutionContext):
|
|
36
35
|
"""Common context for executing one or more queries."""
|
|
37
36
|
|
|
38
|
-
writer: str = 'console'
|
|
37
|
+
writer: str | list[str] = 'console'
|
|
39
38
|
|
|
40
39
|
|
|
41
40
|
class ApiQueryExecutor(executor.Executor):
|
|
@@ -94,20 +93,27 @@ class ApiQueryExecutor(executor.Executor):
|
|
|
94
93
|
args=context.query_parameters,
|
|
95
94
|
**context.fetcher_parameters,
|
|
96
95
|
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
'
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
96
|
+
writer_clients = context.writer_clients
|
|
97
|
+
if not writer_clients:
|
|
98
|
+
logger.warning('No writers configured, skipping write operation')
|
|
99
|
+
return None
|
|
100
|
+
writing_results = []
|
|
101
|
+
for writer_client in writer_clients:
|
|
102
|
+
logger.debug(
|
|
103
|
+
'Start writing data for query %s via %s writer',
|
|
104
|
+
title,
|
|
105
|
+
type(writer_client),
|
|
106
|
+
)
|
|
107
|
+
result = writer_client.write(results, title)
|
|
108
|
+
logger.debug(
|
|
109
|
+
'Finish writing data for query %s via %s writer',
|
|
110
|
+
title,
|
|
111
|
+
type(writer_client),
|
|
112
|
+
)
|
|
113
|
+
writing_results.append(result)
|
|
109
114
|
logger.info('%s executed successfully', title)
|
|
110
|
-
|
|
115
|
+
# Return the last writer's result for backward compatibility
|
|
116
|
+
return writing_results[-1] if writing_results else None
|
|
111
117
|
except Exception as e:
|
|
112
118
|
logger.error('%s generated an exception: %s', title, str(e))
|
|
113
119
|
raise exceptions.GarfExecutorError(
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
|
+
import contextlib
|
|
18
19
|
import os
|
|
19
20
|
|
|
20
21
|
try:
|
|
@@ -103,20 +104,27 @@ class BigQueryExecutor(executor.Executor, query_editor.TemplateProcessorMixin):
|
|
|
103
104
|
else:
|
|
104
105
|
results = report.GarfReport()
|
|
105
106
|
if context.writer and results:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
'
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
107
|
+
writer_clients = context.writer_clients
|
|
108
|
+
if not writer_clients:
|
|
109
|
+
logger.warning('No writers configured, skipping write operation')
|
|
110
|
+
else:
|
|
111
|
+
writing_results = []
|
|
112
|
+
for writer_client in writer_clients:
|
|
113
|
+
logger.debug(
|
|
114
|
+
'Start writing data for query %s via %s writer',
|
|
115
|
+
title,
|
|
116
|
+
type(writer_client),
|
|
117
|
+
)
|
|
118
|
+
writing_result = writer_client.write(results, title)
|
|
119
|
+
logger.debug(
|
|
120
|
+
'Finish writing data for query %s via %s writer',
|
|
121
|
+
title,
|
|
122
|
+
type(writer_client),
|
|
123
|
+
)
|
|
124
|
+
writing_results.append(writing_result)
|
|
125
|
+
logger.info('%s executed successfully', title)
|
|
126
|
+
# Return the last writer's result for backward compatibility
|
|
127
|
+
return writing_results[-1] if writing_results else None
|
|
120
128
|
return results
|
|
121
129
|
except google_cloud_exceptions.GoogleCloudError as e:
|
|
122
130
|
raise BigQueryExecutorError(e) from e
|
|
@@ -139,8 +147,9 @@ class BigQueryExecutor(executor.Executor, query_editor.TemplateProcessorMixin):
|
|
|
139
147
|
except google_cloud_exceptions.NotFound:
|
|
140
148
|
bq_dataset = bigquery.Dataset(dataset_id)
|
|
141
149
|
bq_dataset.location = self.location
|
|
142
|
-
|
|
143
|
-
|
|
150
|
+
with contextlib.suppress(google_cloud_exceptions.Conflict):
|
|
151
|
+
self.client.create_dataset(bq_dataset, timeout=30)
|
|
152
|
+
logger.info('Created new dataset %s', dataset_id)
|
|
144
153
|
|
|
145
154
|
|
|
146
155
|
def extract_datasets(macros: dict | None) -> list[str]:
|
|
@@ -97,18 +97,23 @@ def main():
|
|
|
97
97
|
batch = {query: reader_client.read(query) for query in args.query}
|
|
98
98
|
query_executor.execute_batch(batch, context, args.parallel_threshold)
|
|
99
99
|
else:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
).parse(
|
|
100
|
+
param_types = ['source', 'macro', 'template']
|
|
101
|
+
outputs = args.output.split(',')
|
|
102
|
+
extra_parameters = utils.ParamsParser([*param_types, *outputs]).parse(
|
|
103
|
+
kwargs
|
|
104
|
+
)
|
|
103
105
|
source_parameters = extra_parameters.get('source', {})
|
|
106
|
+
writer_parameters = {}
|
|
107
|
+
for output in outputs:
|
|
108
|
+
writer_parameters.update(extra_parameters.get(output))
|
|
104
109
|
|
|
105
110
|
context = garf_executors.api_executor.ApiExecutionContext(
|
|
106
111
|
query_parameters={
|
|
107
112
|
'macro': extra_parameters.get('macro'),
|
|
108
113
|
'template': extra_parameters.get('template'),
|
|
109
114
|
},
|
|
110
|
-
writer=
|
|
111
|
-
writer_parameters=
|
|
115
|
+
writer=outputs,
|
|
116
|
+
writer_parameters=writer_parameters,
|
|
112
117
|
fetcher_parameters=source_parameters,
|
|
113
118
|
)
|
|
114
119
|
query_executor = garf_executors.setup_executor(
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Copyright 2025 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
|
+
# https://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.
|
|
14
|
+
|
|
15
|
+
"""gRPC endpoint for garf."""
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import logging
|
|
19
|
+
from concurrent import futures
|
|
20
|
+
|
|
21
|
+
import grpc
|
|
22
|
+
from google.protobuf.json_format import MessageToDict
|
|
23
|
+
from grpc_reflection.v1alpha import reflection
|
|
24
|
+
|
|
25
|
+
import garf_executors
|
|
26
|
+
from garf_executors import garf_pb2, garf_pb2_grpc
|
|
27
|
+
from garf_executors.entrypoints.tracer import initialize_tracer
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class GarfService(garf_pb2_grpc.GarfService):
|
|
31
|
+
def Execute(self, request, context):
|
|
32
|
+
query_executor = garf_executors.setup_executor(
|
|
33
|
+
request.source, request.context.fetcher_parameters
|
|
34
|
+
)
|
|
35
|
+
execution_context = garf_executors.execution_context.ExecutionContext(
|
|
36
|
+
**MessageToDict(request.context, preserving_proto_field_name=True)
|
|
37
|
+
)
|
|
38
|
+
result = query_executor.execute(
|
|
39
|
+
query=request.query,
|
|
40
|
+
title=request.title,
|
|
41
|
+
context=execution_context,
|
|
42
|
+
)
|
|
43
|
+
return garf_pb2.ExecuteResponse(results=[result])
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
if __name__ == '__main__':
|
|
47
|
+
parser = argparse.ArgumentParser()
|
|
48
|
+
parser.add_argument('--port', dest='port', default=50051, type=int)
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
'--parallel-threshold', dest='parallel_threshold', default=10, type=int
|
|
51
|
+
)
|
|
52
|
+
args, _ = parser.parse_known_args()
|
|
53
|
+
initialize_tracer()
|
|
54
|
+
server = grpc.server(
|
|
55
|
+
futures.ThreadPoolExecutor(max_workers=args.parallel_threshold)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
service = GarfService()
|
|
59
|
+
garf_pb2_grpc.add_GarfServiceServicer_to_server(service, server)
|
|
60
|
+
SERVICE_NAMES = (
|
|
61
|
+
garf_pb2.DESCRIPTOR.services_by_name['GarfService'].full_name,
|
|
62
|
+
reflection.SERVICE_NAME,
|
|
63
|
+
)
|
|
64
|
+
reflection.enable_server_reflection(SERVICE_NAMES, server)
|
|
65
|
+
server.add_insecure_port(f'[::]:{args.port}')
|
|
66
|
+
server.start()
|
|
67
|
+
logging.info('Garf service started, listening on port %d', 50051)
|
|
68
|
+
server.wait_for_termination()
|
|
@@ -35,7 +35,7 @@ class ExecutionContext(pydantic.BaseModel):
|
|
|
35
35
|
Attributes:
|
|
36
36
|
query_parameters: Parameters to dynamically change query text.
|
|
37
37
|
fetcher_parameters: Parameters to specify fetching setup.
|
|
38
|
-
writer: Type of writer to use.
|
|
38
|
+
writer: Type of writer to use. Can be a single writer string or list of writers.
|
|
39
39
|
writer_parameters: Optional parameters to setup writer.
|
|
40
40
|
"""
|
|
41
41
|
|
|
@@ -45,7 +45,7 @@ class ExecutionContext(pydantic.BaseModel):
|
|
|
45
45
|
fetcher_parameters: dict[str, str | bool | int | list[str | int]] | None = (
|
|
46
46
|
pydantic.Field(default_factory=dict)
|
|
47
47
|
)
|
|
48
|
-
writer: str | None = None
|
|
48
|
+
writer: str | list[str] | None = None
|
|
49
49
|
writer_parameters: dict[str, str] | None = pydantic.Field(
|
|
50
50
|
default_factory=dict
|
|
51
51
|
)
|
|
@@ -75,9 +75,42 @@ class ExecutionContext(pydantic.BaseModel):
|
|
|
75
75
|
|
|
76
76
|
@property
|
|
77
77
|
def writer_client(self) -> abs_writer.AbsWriter:
|
|
78
|
-
|
|
79
|
-
if self.writer
|
|
78
|
+
"""Returns single writer client."""
|
|
79
|
+
if isinstance(self.writer, list) and len(self.writer) > 0:
|
|
80
|
+
writer_type = self.writer[0]
|
|
81
|
+
else:
|
|
82
|
+
writer_type = self.writer
|
|
83
|
+
|
|
84
|
+
writer_params = self.writer_parameters or {}
|
|
85
|
+
|
|
86
|
+
if not writer_type:
|
|
87
|
+
raise ValueError('No writer specified')
|
|
88
|
+
|
|
89
|
+
writer_client = writer.create_writer(writer_type, **writer_params)
|
|
90
|
+
if writer_type == 'bq':
|
|
80
91
|
_ = writer_client.create_or_get_dataset()
|
|
81
|
-
if
|
|
92
|
+
if writer_type == 'sheet':
|
|
82
93
|
writer_client.init_client()
|
|
83
94
|
return writer_client
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def writer_clients(self) -> list[abs_writer.AbsWriter]:
|
|
98
|
+
"""Returns list of writer clients."""
|
|
99
|
+
if not self.writer:
|
|
100
|
+
return []
|
|
101
|
+
|
|
102
|
+
# Convert single writer to list for uniform processing
|
|
103
|
+
writers_to_use = (
|
|
104
|
+
self.writer if isinstance(self.writer, list) else [self.writer]
|
|
105
|
+
)
|
|
106
|
+
writer_params = self.writer_parameters or {}
|
|
107
|
+
|
|
108
|
+
clients = []
|
|
109
|
+
for writer_type in writers_to_use:
|
|
110
|
+
writer_client = writer.create_writer(writer_type, **writer_params)
|
|
111
|
+
if writer_type == 'bq':
|
|
112
|
+
_ = writer_client.create_or_get_dataset()
|
|
113
|
+
if writer_type == 'sheet':
|
|
114
|
+
writer_client.init_client()
|
|
115
|
+
clients.append(writer_client)
|
|
116
|
+
return clients
|
|
@@ -13,14 +13,16 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
import inspect
|
|
16
|
+
import logging
|
|
16
17
|
import sys
|
|
17
18
|
from importlib.metadata import entry_points
|
|
18
19
|
|
|
19
20
|
from garf_core import report_fetcher
|
|
20
|
-
from opentelemetry import trace
|
|
21
21
|
|
|
22
22
|
from garf_executors.telemetry import tracer
|
|
23
23
|
|
|
24
|
+
logger = logging.getLogger(name='garf_executors.fetchers')
|
|
25
|
+
|
|
24
26
|
|
|
25
27
|
@tracer.start_as_current_span('find_fetchers')
|
|
26
28
|
def find_fetchers() -> set[str]:
|
|
@@ -57,8 +59,10 @@ def get_report_fetcher(source: str) -> type[report_fetcher.ApiReportFetcher]:
|
|
|
57
59
|
obj, report_fetcher.ApiReportFetcher
|
|
58
60
|
):
|
|
59
61
|
return getattr(fetcher_module, name)
|
|
60
|
-
except ModuleNotFoundError:
|
|
61
|
-
|
|
62
|
+
except ModuleNotFoundError as e:
|
|
63
|
+
raise report_fetcher.ApiReportFetcherError(
|
|
64
|
+
f'Failed to load fetcher for source {source}, reason: {e}'
|
|
65
|
+
)
|
|
62
66
|
raise report_fetcher.ApiReportFetcherError(
|
|
63
67
|
f'No fetcher available for the source "{source}"'
|
|
64
68
|
)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# NO CHECKED-IN PROTOBUF GENCODE
|
|
4
|
+
# source: garf.proto
|
|
5
|
+
# Protobuf Python Version: 6.31.1
|
|
6
|
+
"""Generated protocol buffer code."""
|
|
7
|
+
from google.protobuf import descriptor as _descriptor
|
|
8
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
9
|
+
from google.protobuf import runtime_version as _runtime_version
|
|
10
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
11
|
+
from google.protobuf.internal import builder as _builder
|
|
12
|
+
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
13
|
+
_runtime_version.Domain.PUBLIC,
|
|
14
|
+
6,
|
|
15
|
+
31,
|
|
16
|
+
1,
|
|
17
|
+
'',
|
|
18
|
+
'garf.proto'
|
|
19
|
+
)
|
|
20
|
+
# @@protoc_insertion_point(imports)
|
|
21
|
+
|
|
22
|
+
_sym_db = _symbol_database.Default()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ngarf.proto\x12\x04garf\x1a\x1cgoogle/protobuf/struct.proto\"g\n\x0e\x45xecuteRequest\x12\x0e\n\x06source\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\r\n\x05query\x18\x03 \x01(\t\x12\'\n\x07\x63ontext\x18\x04 \x01(\x0b\x32\x16.garf.ExecutionContext\"\xbc\x01\n\x10\x45xecutionContext\x12/\n\x10query_parameters\x18\x01 \x01(\x0b\x32\x15.garf.QueryParameters\x12\x33\n\x12\x66\x65tcher_parameters\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x0e\n\x06writer\x18\x03 \x01(\t\x12\x32\n\x11writer_parameters\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\"d\n\x0fQueryParameters\x12&\n\x05macro\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\x12)\n\x08template\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\"\n\x0f\x45xecuteResponse\x12\x0f\n\x07results\x18\x01 \x03(\t2G\n\x0bGarfService\x12\x38\n\x07\x45xecute\x12\x14.garf.ExecuteRequest\x1a\x15.garf.ExecuteResponse\"\x00\x62\x06proto3')
|
|
29
|
+
|
|
30
|
+
_globals = globals()
|
|
31
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
32
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'garf_pb2', _globals)
|
|
33
|
+
if not _descriptor._USE_C_DESCRIPTORS:
|
|
34
|
+
DESCRIPTOR._loaded_options = None
|
|
35
|
+
_globals['_EXECUTEREQUEST']._serialized_start=50
|
|
36
|
+
_globals['_EXECUTEREQUEST']._serialized_end=153
|
|
37
|
+
_globals['_EXECUTIONCONTEXT']._serialized_start=156
|
|
38
|
+
_globals['_EXECUTIONCONTEXT']._serialized_end=344
|
|
39
|
+
_globals['_QUERYPARAMETERS']._serialized_start=346
|
|
40
|
+
_globals['_QUERYPARAMETERS']._serialized_end=446
|
|
41
|
+
_globals['_EXECUTERESPONSE']._serialized_start=448
|
|
42
|
+
_globals['_EXECUTERESPONSE']._serialized_end=482
|
|
43
|
+
_globals['_GARFSERVICE']._serialized_start=484
|
|
44
|
+
_globals['_GARFSERVICE']._serialized_end=555
|
|
45
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
|
2
|
+
"""Client and server classes corresponding to protobuf-defined services."""
|
|
3
|
+
import grpc
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
from . import garf_pb2 as garf__pb2
|
|
7
|
+
|
|
8
|
+
GRPC_GENERATED_VERSION = '1.75.0'
|
|
9
|
+
GRPC_VERSION = grpc.__version__
|
|
10
|
+
_version_not_supported = False
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from grpc._utilities import first_version_is_lower
|
|
14
|
+
_version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
|
|
15
|
+
except ImportError:
|
|
16
|
+
_version_not_supported = True
|
|
17
|
+
|
|
18
|
+
if _version_not_supported:
|
|
19
|
+
raise RuntimeError(
|
|
20
|
+
f'The grpc package installed is at version {GRPC_VERSION},'
|
|
21
|
+
+ f' but the generated code in garf_pb2_grpc.py depends on'
|
|
22
|
+
+ f' grpcio>={GRPC_GENERATED_VERSION}.'
|
|
23
|
+
+ f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
|
|
24
|
+
+ f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class GarfServiceStub(object):
|
|
29
|
+
"""Missing associated documentation comment in .proto file."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, channel):
|
|
32
|
+
"""Constructor.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
channel: A grpc.Channel.
|
|
36
|
+
"""
|
|
37
|
+
self.Execute = channel.unary_unary(
|
|
38
|
+
'/garf.GarfService/Execute',
|
|
39
|
+
request_serializer=garf__pb2.ExecuteRequest.SerializeToString,
|
|
40
|
+
response_deserializer=garf__pb2.ExecuteResponse.FromString,
|
|
41
|
+
_registered_method=True)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class GarfServiceServicer(object):
|
|
45
|
+
"""Missing associated documentation comment in .proto file."""
|
|
46
|
+
|
|
47
|
+
def Execute(self, request, context):
|
|
48
|
+
"""Missing associated documentation comment in .proto file."""
|
|
49
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
50
|
+
context.set_details('Method not implemented!')
|
|
51
|
+
raise NotImplementedError('Method not implemented!')
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def add_GarfServiceServicer_to_server(servicer, server):
|
|
55
|
+
rpc_method_handlers = {
|
|
56
|
+
'Execute': grpc.unary_unary_rpc_method_handler(
|
|
57
|
+
servicer.Execute,
|
|
58
|
+
request_deserializer=garf__pb2.ExecuteRequest.FromString,
|
|
59
|
+
response_serializer=garf__pb2.ExecuteResponse.SerializeToString,
|
|
60
|
+
),
|
|
61
|
+
}
|
|
62
|
+
generic_handler = grpc.method_handlers_generic_handler(
|
|
63
|
+
'garf.GarfService', rpc_method_handlers)
|
|
64
|
+
server.add_generic_rpc_handlers((generic_handler,))
|
|
65
|
+
server.add_registered_method_handlers('garf.GarfService', rpc_method_handlers)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# This class is part of an EXPERIMENTAL API.
|
|
69
|
+
class GarfService(object):
|
|
70
|
+
"""Missing associated documentation comment in .proto file."""
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def Execute(request,
|
|
74
|
+
target,
|
|
75
|
+
options=(),
|
|
76
|
+
channel_credentials=None,
|
|
77
|
+
call_credentials=None,
|
|
78
|
+
insecure=False,
|
|
79
|
+
compression=None,
|
|
80
|
+
wait_for_ready=None,
|
|
81
|
+
timeout=None,
|
|
82
|
+
metadata=None):
|
|
83
|
+
return grpc.experimental.unary_unary(
|
|
84
|
+
request,
|
|
85
|
+
target,
|
|
86
|
+
'/garf.GarfService/Execute',
|
|
87
|
+
garf__pb2.ExecuteRequest.SerializeToString,
|
|
88
|
+
garf__pb2.ExecuteResponse.FromString,
|
|
89
|
+
options,
|
|
90
|
+
channel_credentials,
|
|
91
|
+
insecure,
|
|
92
|
+
call_credentials,
|
|
93
|
+
compression,
|
|
94
|
+
wait_for_ready,
|
|
95
|
+
timeout,
|
|
96
|
+
metadata,
|
|
97
|
+
_registered_method=True)
|
|
@@ -106,19 +106,26 @@ class SqlAlchemyQueryExecutor(
|
|
|
106
106
|
finally:
|
|
107
107
|
conn.connection.execute(f'DROP TABLE {temp_table_name}')
|
|
108
108
|
if context.writer and results:
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
'
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
109
|
+
writer_clients = context.writer_clients
|
|
110
|
+
if not writer_clients:
|
|
111
|
+
logger.warning('No writers configured, skipping write operation')
|
|
112
|
+
else:
|
|
113
|
+
writing_results = []
|
|
114
|
+
for writer_client in writer_clients:
|
|
115
|
+
logger.debug(
|
|
116
|
+
'Start writing data for query %s via %s writer',
|
|
117
|
+
title,
|
|
118
|
+
type(writer_client),
|
|
119
|
+
)
|
|
120
|
+
writing_result = writer_client.write(results, title)
|
|
121
|
+
logger.debug(
|
|
122
|
+
'Finish writing data for query %s via %s writer',
|
|
123
|
+
title,
|
|
124
|
+
type(writer_client),
|
|
125
|
+
)
|
|
126
|
+
writing_results.append(writing_result)
|
|
127
|
+
logger.info('%s executed successfully', title)
|
|
128
|
+
# Return the last writer's result for backward compatibility
|
|
129
|
+
return writing_results[-1] if writing_results else None
|
|
123
130
|
span.set_attribute('execute.num_results', len(results))
|
|
124
131
|
return results
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: garf-executors
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
4
4
|
Summary: Executes queries against API and writes data to local/remote storage.
|
|
5
5
|
Author-email: "Google Inc. (gTech gPS CSE team)" <no-reply@google.com>, Andrei Markin <andrey.markin.ppc@gmail.com>
|
|
6
6
|
License: Apache 2.0
|
|
@@ -8,6 +8,8 @@ garf_executors/exceptions.py
|
|
|
8
8
|
garf_executors/execution_context.py
|
|
9
9
|
garf_executors/executor.py
|
|
10
10
|
garf_executors/fetchers.py
|
|
11
|
+
garf_executors/garf_pb2.py
|
|
12
|
+
garf_executors/garf_pb2_grpc.py
|
|
11
13
|
garf_executors/sql_executor.py
|
|
12
14
|
garf_executors/telemetry.py
|
|
13
15
|
garf_executors.egg-info/PKG-INFO
|
|
@@ -18,6 +20,7 @@ garf_executors.egg-info/requires.txt
|
|
|
18
20
|
garf_executors.egg-info/top_level.txt
|
|
19
21
|
garf_executors/entrypoints/__init__.py
|
|
20
22
|
garf_executors/entrypoints/cli.py
|
|
23
|
+
garf_executors/entrypoints/grpc_server.py
|
|
21
24
|
garf_executors/entrypoints/server.py
|
|
22
25
|
garf_executors/entrypoints/tracer.py
|
|
23
26
|
garf_executors/entrypoints/utils.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|