Skip to content

Commit ac55a60

Browse files
authored
Merge pull request #60 from stackql/copilot/fix-9e75cb89-2d35-4683-b5f0-d2c96cf3d970
[FEATURE] Add kwargs override support for execute and executeStmt methods
2 parents b339593 + 1c2f875 commit ac55a60

File tree

6 files changed

+432
-17
lines changed

6 files changed

+432
-17
lines changed

docs/source/intro.rst

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,92 @@ Here is an example of using the ``json_extract`` function to extract a field fro
168168
res = stackql.execute(query)
169169
print(res)
170170
171+
Overriding Parameters per Query
172+
================================
173+
174+
The :meth:`pystackql.StackQL.execute` and :meth:`pystackql.StackQL.executeStmt` methods support keyword arguments that can override parameters set in the constructor for individual query executions. This is useful when you need to:
175+
176+
- Change the output format for specific queries
177+
- Adjust CSV formatting (separator, headers) for specific exports
178+
- Override authentication for specific providers
179+
- Change other execution parameters on a per-query basis
180+
181+
**Example: Overriding Output Format**
182+
183+
You can create a StackQL instance with a default output format, then override it for specific queries:
184+
185+
.. code-block:: python
186+
187+
from pystackql import StackQL
188+
189+
# Create instance with CSV output by default
190+
provider_auth = {
191+
"github": {
192+
"credentialsenvvar": "GITHUBCREDS",
193+
"type": "basic"
194+
}
195+
}
196+
stackql = StackQL(auth=provider_auth, output="csv")
197+
198+
# This returns CSV format (default)
199+
csv_result = stackql.execute("select id, name from github.repos.repos where org = 'stackql'")
200+
print(csv_result)
201+
# Output:
202+
# id,name
203+
# 443987542,stackql
204+
# 441087132,stackql-provider-registry
205+
# ...
206+
207+
# This overrides to dict format for this query only
208+
dict_result = stackql.execute("select id, name from github.repos.repos where org = 'stackql'", output="dict")
209+
print(dict_result)
210+
# Output:
211+
# [{"id":"443987542","name":"stackql"},{"id":"441087132","name":"stackql-provider-registry"},...]
212+
213+
# Subsequent calls without override use the original CSV format
214+
csv_result2 = stackql.execute("select id, name from github.repos.repos where org = 'stackql' limit 1")
215+
216+
**Example: Overriding CSV Formatting**
217+
218+
You can also override CSV-specific parameters like separator and headers:
219+
220+
.. code-block:: python
221+
222+
from pystackql import StackQL
223+
224+
# Create instance with default CSV settings
225+
stackql = StackQL(output="csv", sep=",", header=False)
226+
227+
# Override to use pipe separator and include headers for this query
228+
result = stackql.execute(
229+
"select id, name from github.repos.repos where org = 'stackql' limit 3",
230+
sep="|",
231+
header=True
232+
)
233+
234+
**Supported Override Parameters**
235+
236+
The following parameters can be overridden in :meth:`pystackql.StackQL.execute` and :meth:`pystackql.StackQL.executeStmt`:
237+
238+
- ``output``: Output format ('dict', 'pandas', or 'csv')
239+
- ``sep``: CSV delimiter/separator (when output='csv')
240+
- ``header``: Include headers in CSV output (when output='csv')
241+
- ``auth``: Custom authentication for providers
242+
- ``custom_registry``: Custom StackQL provider registry URL
243+
- ``max_results``: Maximum results per HTTP request
244+
- ``page_limit``: Maximum pages per resource
245+
- ``max_depth``: Maximum depth for indirect queries
246+
- ``api_timeout``: API request timeout
247+
- ``http_debug``: Enable HTTP debug logging
248+
- Proxy settings: ``proxy_host``, ``proxy_port``, ``proxy_user``, ``proxy_password``, ``proxy_scheme``
249+
- Backend settings: ``backend_storage_mode``, ``backend_file_storage_location``, ``app_root``
250+
- Execution settings: ``execution_concurrency_limit``, ``dataflow_dependency_max``, ``dataflow_components_max``
251+
252+
.. note::
253+
254+
Parameter overrides only affect the specific query execution and do not modify the StackQL instance's configuration. Subsequent queries will use the original constructor parameters unless overridden again.
255+
256+
171257
Using the Jupyter Magic Extension
172258
=================================
173259

pystackql/core/query.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,19 @@ def _debug_log(self, message):
5050
with open(self.debug_log_file, "a") as log_file:
5151
log_file.write(message + "\n")
5252

53-
def execute(self, query, custom_auth=None, env_vars=None):
53+
def execute(self, query, custom_auth=None, env_vars=None, override_params=None):
5454
"""Execute a StackQL query.
5555
5656
Args:
5757
query (str): The query to execute
5858
custom_auth (dict, optional): Custom authentication dictionary. Defaults to None.
5959
env_vars (dict, optional): Environment variables for the subprocess. Defaults to None.
60+
override_params (list, optional): Override parameters for this execution. Defaults to None.
6061
6162
Returns:
6263
dict: The query results
6364
"""
64-
local_params = self.params.copy()
65+
local_params = (override_params if override_params is not None else self.params).copy()
6566
script_path = None
6667

6768
# Format query for platform

pystackql/core/stackql.py

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ def upgrade(self, showprogress=True):
263263

264264
return message
265265

266-
def executeStmt(self, query, custom_auth=None, env_vars=None):
266+
def executeStmt(self, query, custom_auth=None, env_vars=None, **kwargs):
267267
"""Executes a query using the StackQL instance and returns the output as a string.
268268
This is intended for operations which do not return a result set, for example a mutation
269269
operation such as an `INSERT` or a `DELETE` or life cycle method such as an `EXEC` operation
@@ -279,6 +279,12 @@ def executeStmt(self, query, custom_auth=None, env_vars=None):
279279
:type custom_auth: dict, optional
280280
:param env_vars: Command-specific environment variables for this execution.
281281
:type env_vars: dict, optional
282+
:param kwargs: Additional keyword arguments that override constructor parameters for this execution.
283+
Supported overrides: output, sep, header, auth, custom_registry, max_results, page_limit,
284+
max_depth, api_timeout, http_debug, proxy_host, proxy_port, proxy_user, proxy_password,
285+
proxy_scheme, backend_storage_mode, backend_file_storage_location, app_root,
286+
execution_concurrency_limit, dataflow_dependency_max, dataflow_components_max
287+
:type kwargs: optional
282288
283289
:return: The output result of the query in string format. If in `server_mode`, it
284290
returns a JSON string representation of the result.
@@ -292,25 +298,47 @@ def executeStmt(self, query, custom_auth=None, env_vars=None):
292298
>>> result
293299
"""
294300
if self.server_mode:
301+
# Server mode: handle output override
302+
output_format = kwargs.get('output', self.output)
303+
295304
result = self.server_connection.execute_query(query, is_statement=True)
296305

297306
# Format result based on output type
298-
if self.output == 'pandas':
307+
if output_format == 'pandas':
299308
import pandas as pd
300309
return pd.DataFrame(result)
301-
elif self.output == 'csv':
310+
elif output_format == 'csv':
302311
# Return the string representation of the result
303312
return result[0]['message']
304313
else:
305314
return result
306315
else:
316+
# Local mode: handle parameter overrides
317+
override_params = None
318+
output_format = kwargs.get('output', self.output)
319+
320+
# If custom_auth is provided as kwarg, use it
321+
if 'auth' in kwargs:
322+
custom_auth = kwargs['auth']
323+
324+
# Generate override params if kwargs provided
325+
if kwargs:
326+
from ..utils import generate_params_for_execution
327+
override_params = generate_params_for_execution(self._base_kwargs, kwargs)
328+
307329
# Execute the query
308-
result = self.local_query_executor.execute(query, custom_auth=custom_auth, env_vars=env_vars)
330+
result = self.local_query_executor.execute(query, custom_auth=custom_auth, env_vars=env_vars, override_params=override_params)
309331

310-
# Format the result
311-
return self.local_output_formatter.format_statement_result(result)
332+
# Format the result with appropriate output formatter
333+
if output_format != self.output:
334+
# Create a temporary formatter for this execution
335+
from .output import OutputFormatter
336+
temp_formatter = OutputFormatter(output_format)
337+
return temp_formatter.format_statement_result(result)
338+
else:
339+
return self.local_output_formatter.format_statement_result(result)
312340

313-
def execute(self, query, suppress_errors=True, custom_auth=None, env_vars=None):
341+
def execute(self, query, suppress_errors=True, custom_auth=None, env_vars=None, **kwargs):
314342
"""
315343
Executes a StackQL query and returns the output based on the specified output format.
316344
@@ -325,6 +353,12 @@ def execute(self, query, suppress_errors=True, custom_auth=None, env_vars=None):
325353
:type custom_auth: dict, optional
326354
:param env_vars: Command-specific environment variables for this execution.
327355
:type env_vars: dict, optional
356+
:param kwargs: Additional keyword arguments that override constructor parameters for this execution.
357+
Supported overrides: output, sep, header, auth, custom_registry, max_results, page_limit,
358+
max_depth, api_timeout, http_debug, proxy_host, proxy_port, proxy_user, proxy_password,
359+
proxy_scheme, backend_storage_mode, backend_file_storage_location, app_root,
360+
execution_concurrency_limit, dataflow_dependency_max, dataflow_components_max
361+
:type kwargs: optional
328362
329363
:return: The output of the query, which can be a list of dictionary objects, a Pandas DataFrame,
330364
or a raw CSV string, depending on the configured output format.
@@ -344,29 +378,52 @@ def execute(self, query, suppress_errors=True, custom_auth=None, env_vars=None):
344378
>>> result = stackql.execute(query)
345379
"""
346380
if self.server_mode:
381+
# Server mode: handle output override
382+
output_format = kwargs.get('output', self.output)
383+
347384
result = self.server_connection.execute_query(query)
348385

349386
# Format result based on output type
350-
if self.output == 'pandas':
387+
if output_format == 'pandas':
351388
import pandas as pd
352389
import json
353390
from io import StringIO
354391
json_str = json.dumps(result)
355392
return pd.read_json(StringIO(json_str))
356-
elif self.output == 'csv':
393+
elif output_format == 'csv':
357394
raise ValueError("CSV output is not supported in server_mode.")
358395
else: # Assume 'dict' output
359396
return result
360397
else:
398+
# Local mode: handle parameter overrides
399+
override_params = None
400+
output_format = kwargs.get('output', self.output)
401+
http_debug = kwargs.get('http_debug', self.http_debug)
402+
403+
# If custom_auth is provided as kwarg, use it
404+
if 'auth' in kwargs:
405+
custom_auth = kwargs['auth']
406+
407+
# Generate override params if kwargs provided
408+
if kwargs:
409+
from ..utils import generate_params_for_execution
410+
override_params = generate_params_for_execution(self._base_kwargs, kwargs)
411+
361412
# Apply HTTP debug setting
362-
if self.http_debug:
413+
if http_debug:
363414
suppress_errors = False
364415

365416
# Execute the query
366-
output = self.local_query_executor.execute(query, custom_auth=custom_auth, env_vars=env_vars)
417+
output = self.local_query_executor.execute(query, custom_auth=custom_auth, env_vars=env_vars, override_params=override_params)
367418

368-
# Format the result
369-
return self.local_output_formatter.format_query_result(output, suppress_errors)
419+
# Format the result with appropriate output formatter
420+
if output_format != self.output:
421+
# Create a temporary formatter for this execution
422+
from .output import OutputFormatter
423+
temp_formatter = OutputFormatter(output_format)
424+
return temp_formatter.format_query_result(output, suppress_errors)
425+
else:
426+
return self.local_output_formatter.format_query_result(output, suppress_errors)
370427

371428
async def executeQueriesAsync(self, queries):
372429
"""Executes multiple StackQL queries asynchronously using the current StackQL instance.

pystackql/utils/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
)
2424

2525
from .auth import format_auth
26-
from .params import setup_local_mode
26+
from .params import setup_local_mode, generate_params_for_execution
2727

2828
__all__ = [
2929
# Platform utilities
@@ -45,5 +45,6 @@
4545
'format_auth',
4646

4747
# Parameter utilities
48-
'setup_local_mode'
48+
'setup_local_mode',
49+
'generate_params_for_execution'
4950
]

0 commit comments

Comments
 (0)