diff --git a/CHANGELOG.md b/CHANGELOG.md index ea7fbf9..a92c9a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ # Changelog -## v3.5.7 (2024-04-17) +## v3.6.0 (2024-04-18) ### Updates * modified dict response for `executeStmt` + * modified error response for `execute` ## v3.5.4 (2024-04-11) diff --git a/README.rst b/README.rst index 843e6a3..777044d 100644 --- a/README.rst +++ b/README.rst @@ -194,4 +194,4 @@ To publish the package to PyPI, run the following command: :: - twine upload --config-file .pypirc dist/pystackql-3.5.7.tar.gz + twine upload --config-file .pypirc dist/pystackql-3.6.0.tar.gz diff --git a/docs/source/conf.py b/docs/source/conf.py index a9bc4c4..ca299d9 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -26,7 +26,7 @@ # The short X.Y version version = '' # The full version, including alpha/beta/rc tags -release = '3.5.7' +release = '3.6.0' # -- General configuration --------------------------------------------------- diff --git a/pystackql/stackql.py b/pystackql/stackql.py index 102d836..bf0a561 100644 --- a/pystackql/stackql.py +++ b/pystackql/stackql.py @@ -176,8 +176,17 @@ def _run_query(self, query): :type query: str :return: The output result of the query, which can either be the actual query result or an error message. - :rtype: str + :rtype: dict + + Example: + :: + { + "data": "[{\"machine_type\": \"n1-standard-1\", \"status\": \"RUNNING\", \"num_instances\": 3}, ...]", + "error": "stderr message if present", + "exception": "ERROR: {\"exception\": \"\", \"doc\": \"\", \"params\": \"\", \"stdout\": \"\", \"stderr\": \"\"} + } + Possible error messages include: - Indications that the StackQL binary wasn't found. - Generic error messages for other exceptions encountered during the query execution. @@ -219,11 +228,6 @@ def _run_query(self, query): } output["exception"] = f"ERROR: {json.dumps(error_details)}" - # output = {'data': , 'error': , 'exception': } - # for a statement you would expect 'error' to exist - # for a query you would expect 'data' to exist - # 'exception' in output indicates an error occurred during the execution (statement or query) - return output def __init__(self, @@ -487,12 +491,20 @@ def executeStmt(self, query): else: return result else: + # returns either... # {'error': ''} if something went wrong; or # {'message': ''} if the statement was executed successfully + result = self._run_query(query) if "exception" in result: - return {"error": result["exception"]} + exception_msg = result["exception"] + if self.output == 'pandas': + return pd.DataFrame({'error': [exception_msg]}) if exception_msg else pd.DataFrame({'error': []}) + elif self.output == 'csv': + return exception_msg + else: + return {"error": exception_msg} # message on stderr message = result["error"] @@ -543,35 +555,57 @@ def execute(self, query, suppress_errors=True): return result else: # returns either... - # {'error': } if something went wrong; or - # [{}] if the statement was executed successfully, messages to stderr are ignored + # [{'error': }] if something went wrong; or + # [{},...] if the statement was executed successfully, messages to stderr are ignored output = self._run_query(query) if "exception" in output: - return {"error": output["exception"]} + exception_msg = output["exception"] + if self.output == 'pandas': + return pd.DataFrame({'error': [exception_msg]}) if exception_msg else pd.DataFrame({'error': []}) + elif self.output == 'csv': + return exception_msg + else: + return [{"error": exception_msg}] if "data" in output: + data = output["data"] # theres data, return it if self.output == 'csv': - return output["data"] + return data elif self.output == 'pandas': try: - return pd.read_json(StringIO(output["data"])) + return pd.read_json(StringIO(data)) except ValueError: return pd.DataFrame([{"error": "Invalid JSON output"}]) else: # Assume 'dict' output try: - return json.loads(output["data"]) + return json.loads(data) except ValueError: - return {"error": f"Invalid JSON output : {output['data']}"} - else: - if "error" in output: - if suppress_errors: - return [] - else: - return {"error": output["error"]} + return [{"error": f"Invalid JSON output : {data}"}] + + if "error" in output: + # theres no data but there is stderr from the request, could be an expected error like a 404 + if suppress_errors: + # we dont care about errors, return an empty list + csv_ret = "" + pd_ret = pd.DataFrame() + dict_ret = [] + else: + # we care about errors, return the error + err_msg = output["error"] + csv_ret = err_msg + pd_ret = pd.DataFrame([{"error": err_msg}]) + dict_ret = [{"error": err_msg}] + + if self.output == 'csv': + return csv_ret + elif self.output == 'pandas': + return pd_ret + else: # Assume 'dict' output + return dict_ret + - # # asnyc query support # diff --git a/setup.py b/setup.py index 1f0bb9f..44984bc 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ setup( name='pystackql', - version='3.5.7', + version='3.6.0', description='A Python interface for StackQL', long_description=readme, author='Jeffrey Aven',