garf-executors 0.1.4__py3-none-any.whl → 1.0.2__py3-none-any.whl

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.
Files changed (44) hide show
  1. garf/executors/__init__.py +60 -0
  2. garf/executors/api_executor.py +143 -0
  3. garf/executors/bq_executor.py +177 -0
  4. garf/executors/config.py +52 -0
  5. garf/executors/entrypoints/__init__.py +0 -0
  6. garf/executors/entrypoints/cli.py +177 -0
  7. garf/executors/entrypoints/grpc_server.py +67 -0
  8. garf/executors/entrypoints/server.py +117 -0
  9. garf/executors/entrypoints/tracer.py +57 -0
  10. garf/executors/entrypoints/utils.py +140 -0
  11. garf/executors/exceptions.py +17 -0
  12. garf/executors/execution_context.py +117 -0
  13. garf/executors/executor.py +124 -0
  14. garf/executors/fetchers.py +78 -0
  15. garf/executors/garf_pb2.py +45 -0
  16. garf/executors/garf_pb2_grpc.py +97 -0
  17. garf/executors/query_processor.py +61 -0
  18. garf/executors/sql_executor.py +142 -0
  19. garf/executors/telemetry.py +20 -0
  20. garf/executors/workflow.py +109 -0
  21. garf_executors/__init__.py +9 -44
  22. garf_executors/api_executor.py +9 -99
  23. garf_executors/bq_executor.py +9 -144
  24. garf_executors/config.py +9 -35
  25. garf_executors/entrypoints/__init__.py +25 -0
  26. garf_executors/entrypoints/cli.py +9 -116
  27. garf_executors/entrypoints/grcp_server.py +25 -0
  28. garf_executors/entrypoints/server.py +9 -92
  29. garf_executors/entrypoints/tracer.py +9 -26
  30. garf_executors/entrypoints/utils.py +9 -124
  31. garf_executors/exceptions.py +11 -3
  32. garf_executors/execution_context.py +9 -67
  33. garf_executors/executor.py +9 -71
  34. garf_executors/fetchers.py +9 -59
  35. garf_executors/sql_executor.py +9 -107
  36. garf_executors/telemetry.py +10 -5
  37. garf_executors/workflow.py +25 -0
  38. {garf_executors-0.1.4.dist-info → garf_executors-1.0.2.dist-info}/METADATA +17 -7
  39. garf_executors-1.0.2.dist-info/RECORD +42 -0
  40. garf_executors-1.0.2.dist-info/entry_points.txt +2 -0
  41. {garf_executors-0.1.4.dist-info → garf_executors-1.0.2.dist-info}/top_level.txt +1 -0
  42. garf_executors-0.1.4.dist-info/RECORD +0 -20
  43. garf_executors-0.1.4.dist-info/entry_points.txt +0 -2
  44. {garf_executors-0.1.4.dist-info → garf_executors-1.0.2.dist-info}/WHEEL +0 -0
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Google LLC
1
+ # Copyright 2026 Google LLC
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -11,150 +11,15 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- """Executes queries in BigQuery."""
15
14
 
16
- from __future__ import annotations
17
15
 
18
- import os
16
+ import warnings
19
17
 
20
- try:
21
- from google.cloud import bigquery # type: ignore
22
- except ImportError as e:
23
- raise ImportError(
24
- 'Please install garf-executors with BigQuery support '
25
- '- `pip install garf-executors[bq]`'
26
- ) from e
18
+ from garf.executors.bq_executor import *
27
19
 
28
- import logging
29
-
30
- from garf_core import query_editor, report
31
- from google.cloud import exceptions as google_cloud_exceptions
32
-
33
- from garf_executors import exceptions, execution_context, executor
34
- from garf_executors.telemetry import tracer
35
-
36
- logger = logging.getLogger(__name__)
37
-
38
-
39
- class BigQueryExecutorError(exceptions.GarfExecutorError):
40
- """Error when BigQueryExecutor fails to run query."""
41
-
42
-
43
- class BigQueryExecutor(executor.Executor, query_editor.TemplateProcessorMixin):
44
- """Handles query execution in BigQuery.
45
-
46
- Attributes:
47
- project_id: Google Cloud project id.
48
- location: BigQuery dataset location.
49
- client: BigQuery client.
50
- """
51
-
52
- def __init__(
53
- self,
54
- project_id: str | None = os.getenv('GOOGLE_CLOUD_PROJECT'),
55
- location: str | None = None,
56
- ) -> None:
57
- """Initializes BigQueryExecutor.
58
-
59
- Args:
60
- project_id: Google Cloud project id.
61
- location: BigQuery dataset location.
62
- """
63
- if not project_id:
64
- raise BigQueryExecutorError(
65
- 'project_id is required. Either provide it as project_id parameter '
66
- 'or GOOGLE_CLOUD_PROJECT env variable.'
67
- )
68
- self.project_id = project_id
69
- self.location = location
70
-
71
- @property
72
- def client(self) -> bigquery.Client:
73
- """Instantiates bigquery client."""
74
- return bigquery.Client(self.project_id)
75
-
76
- @tracer.start_as_current_span('bq.execute')
77
- def execute(
78
- self,
79
- query: str,
80
- title: str,
81
- context: execution_context.ExecutionContext = (
82
- execution_context.ExecutionContext()
83
- ),
84
- ) -> report.GarfReport:
85
- """Executes query in BigQuery.
86
-
87
- Args:
88
- query: Location of the query.
89
- title: Name of the query.
90
- context: Query execution context.
91
-
92
- Returns:
93
- Report with data if query returns some data otherwise empty Report.
94
- """
95
- query_text = self.replace_params_template(query, context.query_parameters)
96
- self.create_datasets(context.query_parameters.macro)
97
- job = self.client.query(query_text)
98
- try:
99
- result = job.result()
100
- logger.debug('%s launched successfully', title)
101
- if result.total_rows:
102
- results = report.GarfReport.from_pandas(result.to_dataframe())
103
- else:
104
- results = report.GarfReport()
105
- if context.writer and results:
106
- writer_client = context.writer_client
107
- logger.debug(
108
- 'Start writing data for query %s via %s writer',
109
- title,
110
- type(writer_client),
111
- )
112
- writing_result = writer_client.write(results, title)
113
- logger.debug(
114
- 'Finish writing data for query %s via %s writer',
115
- title,
116
- type(writer_client),
117
- )
118
- logger.info('%s executed successfully', title)
119
- return writing_result
120
- return results
121
- except google_cloud_exceptions.GoogleCloudError as e:
122
- raise BigQueryExecutorError(e) from e
123
-
124
- @tracer.start_as_current_span('bq.create_datasets')
125
- def create_datasets(self, macros: dict | None) -> None:
126
- """Creates datasets in BQ based on values in a dict.
127
-
128
- If dict contains keys with 'dataset' in them, then values for such keys
129
- are treated as dataset names.
130
-
131
- Args:
132
- macros: Mapping containing data for query execution.
133
- """
134
- if macros and (datasets := extract_datasets(macros)):
135
- for dataset in datasets:
136
- dataset_id = f'{self.project_id}.{dataset}'
137
- try:
138
- self.client.get_dataset(dataset_id)
139
- except google_cloud_exceptions.NotFound:
140
- bq_dataset = bigquery.Dataset(dataset_id)
141
- bq_dataset.location = self.location
142
- self.client.create_dataset(bq_dataset, timeout=30)
143
- logger.info('Created new dataset %s', dataset_id)
144
-
145
-
146
- def extract_datasets(macros: dict | None) -> list[str]:
147
- """Finds dataset-related keys based on values in a dict.
148
-
149
- If dict contains keys with 'dataset' in them, then values for such keys
150
- are treated as dataset names.
151
-
152
- Args:
153
- macros: Mapping containing data for query execution.
154
-
155
- Returns:
156
- Possible names of datasets.
157
- """
158
- if not macros:
159
- return []
160
- return [value for macro, value in macros.items() if 'dataset' in macro]
20
+ warnings.warn(
21
+ "The 'garf_executors' namespace is deprecated. "
22
+ "Please use 'garf.executors' instead.",
23
+ DeprecationWarning,
24
+ stacklevel=2,
25
+ )
garf_executors/config.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2025 Google LLC
1
+ # Copyright 2026 Google LLC
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -12,40 +12,14 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- # pylint: disable=C0330, g-bad-import-order, g-multiple-import
16
15
 
17
- """Stores mapping between API aliases and their execution context."""
16
+ import warnings
18
17
 
19
- from __future__ import annotations
18
+ from garf.executors.config import *
20
19
 
21
- import os
22
- import pathlib
23
-
24
- import pydantic
25
- import smart_open
26
- import yaml
27
-
28
- from garf_executors.execution_context import ExecutionContext
29
-
30
-
31
- class Config(pydantic.BaseModel):
32
- """Stores necessary parameters for one or multiple API sources.
33
-
34
- Attributes:
35
- source: Mapping between API source alias and execution parameters.
36
- """
37
-
38
- sources: dict[str, ExecutionContext]
39
-
40
- @classmethod
41
- def from_file(cls, path: str | pathlib.Path | os.PathLike[str]) -> Config:
42
- """Builds config from local or remote yaml file."""
43
- with smart_open.open(path, 'r', encoding='utf-8') as f:
44
- data = yaml.safe_load(f)
45
- return Config(sources=data)
46
-
47
- def save(self, path: str | pathlib.Path | os.PathLike[str]) -> str:
48
- """Saves config to local or remote yaml file."""
49
- with smart_open.open(path, 'w', encoding='utf-8') as f:
50
- yaml.dump(self.model_dump().get('sources'), f, encoding='utf-8')
51
- return f'Config is saved to {str(path)}'
20
+ warnings.warn(
21
+ "The 'garf_executors' namespace is deprecated. "
22
+ "Please use 'garf.executors' instead.",
23
+ DeprecationWarning,
24
+ stacklevel=2,
25
+ )
@@ -0,0 +1,25 @@
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
+ # 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
+
16
+ import warnings
17
+
18
+ from garf.executors.entrypoints import *
19
+
20
+ warnings.warn(
21
+ "The 'garf_executors.entrypoints' namespace is deprecated. "
22
+ "Please use 'garf.executors.entrypoints' instead.",
23
+ DeprecationWarning,
24
+ stacklevel=2,
25
+ )
@@ -1,4 +1,4 @@
1
- # Copyright 2025 Google LLC
1
+ # Copyright 2026 Google LLC
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -11,122 +11,15 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- """Module for defining `garf` CLI utility.
15
14
 
16
- `garf` allows to execute queries and store results in local/remote
17
- storage.
18
- """
19
15
 
20
- from __future__ import annotations
16
+ import warnings
21
17
 
22
- import argparse
23
- import logging
24
- import sys
18
+ from garf.executors.entrypoints.cli import *
25
19
 
26
- from garf_io import reader
27
-
28
- import garf_executors
29
- from garf_executors import config, exceptions
30
- from garf_executors.entrypoints import utils
31
- from garf_executors.entrypoints.tracer import initialize_tracer
32
- from garf_executors.telemetry import tracer
33
-
34
- initialize_tracer()
35
-
36
-
37
- @tracer.start_as_current_span('garf.entrypoints.cli')
38
- def main():
39
- parser = argparse.ArgumentParser()
40
- parser.add_argument('query', nargs='*')
41
- parser.add_argument('-c', '--config', dest='config', default=None)
42
- parser.add_argument('--source', dest='source', default=None)
43
- parser.add_argument('--output', dest='output', default='console')
44
- parser.add_argument('--input', dest='input', default='file')
45
- parser.add_argument('--log', '--loglevel', dest='loglevel', default='info')
46
- parser.add_argument('--logger', dest='logger', default='local')
47
- parser.add_argument('--log-name', dest='log_name', default='garf')
48
- parser.add_argument(
49
- '--parallel-queries', dest='parallel_queries', action='store_true'
50
- )
51
- parser.add_argument(
52
- '--no-parallel-queries', dest='parallel_queries', action='store_false'
53
- )
54
- parser.add_argument('--dry-run', dest='dry_run', action='store_true')
55
- parser.add_argument('-v', '--version', dest='version', action='store_true')
56
- parser.add_argument(
57
- '--parallel-threshold', dest='parallel_threshold', default=10, type=int
58
- )
59
- parser.add_argument(
60
- '--enable-cache', dest='enable_cache', action='store_true'
61
- )
62
- parser.add_argument(
63
- '--cache-ttl-seconds',
64
- dest='cache_ttl_seconds',
65
- default=3600,
66
- type=int,
67
- )
68
- parser.set_defaults(parallel_queries=True)
69
- parser.set_defaults(enable_cache=False)
70
- parser.set_defaults(dry_run=False)
71
- args, kwargs = parser.parse_known_args()
72
-
73
- if args.version:
74
- print(garf_executors.__version__)
75
- sys.exit()
76
- logger = utils.init_logging(
77
- loglevel=args.loglevel.upper(), logger_type=args.logger, name=args.log_name
78
- )
79
- if not args.query:
80
- logger.error('Please provide one or more queries to run')
81
- raise exceptions.GarfExecutorError(
82
- 'Please provide one or more queries to run'
83
- )
84
- reader_client = reader.create_reader(args.input)
85
- if config_file := args.config:
86
- execution_config = config.Config.from_file(config_file)
87
- if not (context := execution_config.sources.get(args.source)):
88
- raise exceptions.GarfExecutorError(
89
- f'No execution context found for source {args.source} in {config_file}'
90
- )
91
- query_executor = garf_executors.setup_executor(
92
- source=args.source,
93
- fetcher_parameters=context.fetcher_parameters,
94
- enable_cache=args.enable_cache,
95
- cache_ttl_seconds=args.cache_ttl_seconds,
96
- )
97
- batch = {query: reader_client.read(query) for query in args.query}
98
- query_executor.execute_batch(batch, context, args.parallel_threshold)
99
- else:
100
- extra_parameters = utils.ParamsParser(
101
- ['source', args.output, 'macro', 'template']
102
- ).parse(kwargs)
103
- source_parameters = extra_parameters.get('source', {})
104
-
105
- context = garf_executors.api_executor.ApiExecutionContext(
106
- query_parameters={
107
- 'macro': extra_parameters.get('macro'),
108
- 'template': extra_parameters.get('template'),
109
- },
110
- writer=args.output,
111
- writer_parameters=extra_parameters.get(args.output),
112
- fetcher_parameters=source_parameters,
113
- )
114
- query_executor = garf_executors.setup_executor(
115
- source=args.source,
116
- fetcher_parameters=context.fetcher_parameters,
117
- enable_cache=args.enable_cache,
118
- cache_ttl_seconds=args.cache_ttl_seconds,
119
- )
120
- if args.parallel_queries:
121
- logger.info('Running queries in parallel')
122
- batch = {query: reader_client.read(query) for query in args.query}
123
- query_executor.execute_batch(batch, context, args.parallel_threshold)
124
- else:
125
- logger.info('Running queries sequentially')
126
- for query in args.query:
127
- query_executor.execute(reader_client.read(query), query, context)
128
- logging.shutdown()
129
-
130
-
131
- if __name__ == '__main__':
132
- main()
20
+ warnings.warn(
21
+ "The 'garf_executors.entrypoints' namespace is deprecated. "
22
+ "Please use 'garf.executors.entrypoints' instead.",
23
+ DeprecationWarning,
24
+ stacklevel=2,
25
+ )
@@ -0,0 +1,25 @@
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
+ # 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
+
16
+ import warnings
17
+
18
+ from garf.executors.entrypoints.grpc_server import *
19
+
20
+ warnings.warn(
21
+ "The 'garf_executors.entrypoints' namespace is deprecated. "
22
+ "Please use 'garf.executors.entrypoints' instead.",
23
+ DeprecationWarning,
24
+ stacklevel=2,
25
+ )
@@ -1,4 +1,4 @@
1
- # Copyright 2025 Google LLC
1
+ # Copyright 2026 Google LLC
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -12,97 +12,14 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- """FastAPI endpoint for executing queries."""
16
15
 
17
- from typing import Optional, Union
16
+ import warnings
18
17
 
19
- import fastapi
20
- import pydantic
21
- import uvicorn
22
- from garf_io import reader
23
- from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
18
+ from garf.executors.entrypoints.server import *
24
19
 
25
- import garf_executors
26
- from garf_executors import exceptions
27
- from garf_executors.entrypoints.tracer import initialize_tracer
28
-
29
- initialize_tracer()
30
- app = fastapi.FastAPI()
31
- FastAPIInstrumentor.instrument_app(app)
32
-
33
-
34
- class ApiExecutorRequest(pydantic.BaseModel):
35
- """Request for executing a query.
36
-
37
- Attributes:
38
- source: Type of API to interact with.
39
- title: Name of the query used as an output for writing.
40
- query: Query to execute.
41
- query_path: Local or remote path to query.
42
- context: Execution context.
43
- """
44
-
45
- source: str
46
- title: Optional[str] = None
47
- query: Optional[str] = None
48
- query_path: Optional[Union[str, list[str]]] = None
49
- context: garf_executors.api_executor.ApiExecutionContext
50
-
51
- @pydantic.model_validator(mode='after')
52
- def check_query_specified(self):
53
- if not self.query_path and not self.query:
54
- raise exceptions.GarfExecutorError(
55
- 'Missing one of required parameters: query, query_path'
56
- )
57
- return self
58
-
59
- def model_post_init(self, __context__) -> None:
60
- if self.query_path and isinstance(self.query_path, str):
61
- self.query = reader.FileReader().read(self.query_path)
62
- if not self.title:
63
- self.title = str(self.query_path)
64
-
65
-
66
- class ApiExecutorResponse(pydantic.BaseModel):
67
- """Response after executing a query.
68
-
69
- Attributes:
70
- results: Results of query execution.
71
- """
72
-
73
- results: list[str]
74
-
75
-
76
- @app.get('/api/version')
77
- async def version() -> str:
78
- return garf_executors.__version__
79
-
80
-
81
- @app.get('/api/fetchers')
82
- async def get_fetchers() -> list[str]:
83
- """Shows all available API sources."""
84
- return list(garf_executors.fetchers.find_fetchers())
85
-
86
-
87
- @app.post('/api/execute')
88
- async def execute(request: ApiExecutorRequest) -> ApiExecutorResponse:
89
- query_executor = garf_executors.setup_executor(
90
- request.source, request.context.fetcher_parameters
91
- )
92
- result = query_executor.execute(request.query, request.title, request.context)
93
- return ApiExecutorResponse(results=[result])
94
-
95
-
96
- @app.post('/api/execute:batch')
97
- def execute_batch(request: ApiExecutorRequest) -> ApiExecutorResponse:
98
- query_executor = garf_executors.setup_executor(
99
- request.source, request.context.fetcher_parameters
100
- )
101
- reader_client = reader.FileReader()
102
- batch = {query: reader_client.read(query) for query in request.query_path}
103
- results = query_executor.execute_batch(batch, request.context)
104
- return ApiExecutorResponse(results=results)
105
-
106
-
107
- if __name__ == '__main__':
108
- uvicorn.run(app)
20
+ warnings.warn(
21
+ "The 'garf_executors.entrypoints' namespace is deprecated. "
22
+ "Please use 'garf.executors.entrypoints' instead.",
23
+ DeprecationWarning,
24
+ stacklevel=2,
25
+ )
@@ -1,4 +1,4 @@
1
- # Copyright 2025 Google LLC
1
+ # Copyright 2026 Google LLC
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -12,31 +12,14 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- import os
16
15
 
17
- from opentelemetry import trace
18
- from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
19
- OTLPSpanExporter,
20
- )
21
- from opentelemetry.sdk.resources import Resource
22
- from opentelemetry.sdk.trace import TracerProvider
23
- from opentelemetry.sdk.trace.export import (
24
- BatchSpanProcessor,
25
- )
26
-
27
- DEFAULT_SERVICE_NAME = 'garf'
16
+ import warnings
28
17
 
18
+ from garf.executors.entrypoints.tracer import *
29
19
 
30
- def initialize_tracer():
31
- resource = Resource.create(
32
- {'service.name': os.getenv('OTLP_SERVICE_NAME', DEFAULT_SERVICE_NAME)}
33
- )
34
-
35
- tracer_provider = TracerProvider(resource=resource)
36
-
37
- if otel_endpoint := os.getenv('OTEL_EXPORTER_OTLP_ENDPOINT'):
38
- otlp_processor = BatchSpanProcessor(
39
- OTLPSpanExporter(endpoint=otel_endpoint, insecure=True)
40
- )
41
- tracer_provider.add_span_processor(otlp_processor)
42
- trace.set_tracer_provider(tracer_provider)
20
+ warnings.warn(
21
+ "The 'garf_executors.entrypoints' namespace is deprecated. "
22
+ "Please use 'garf.executors.entrypoints' instead.",
23
+ DeprecationWarning,
24
+ stacklevel=2,
25
+ )