Skip to content

Commit 7eff11a

Browse files
authored
Merge pull request #21 from stackql/feature/refactor
async server tests
2 parents 933e436 + 8b5fee3 commit 7eff11a

File tree

11 files changed

+67
-22
lines changed

11 files changed

+67
-22
lines changed

.github/workflows/test.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ jobs:
7575
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
7676
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
7777
GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }}
78+
AWS_REGION: ${{ vars.AWS_REGION }}
7879
AWS_REGIONS: ${{ vars.AWS_REGIONS }}
7980
GCP_PROJECT: ${{ vars.GCP_PROJECT }}
8081
GCP_ZONE: ${{ vars.GCP_ZONE }}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ ipython_config.py
114114
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
115115
# in version control.
116116
# https://pdm.fming.dev/#use-with-ide
117+
stackql
118+
stackql-zip
119+
117120
.pdm.toml
118121

119122
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# Changelog
22

3-
## v3.1.1 (2023-10-16)
3+
## v3.1.2 (2023-10-16)
4+
5+
### Updates
6+
7+
* `pandas` type fixes
8+
9+
## v3.1.1 (2023-10-14)
410

511
### Updates
612

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ PyStackQL has been tested on:
112112
- Python 3.9
113113
- Python 3.10
114114
- Python 3.11
115-
- Python 3.12 (MacOS and Linux only
115+
- Python 3.12 (MacOS and Linux only)
116116

117117
Licensing
118118
~~~~~~~~~
@@ -193,4 +193,4 @@ To publish the package to PyPI, run the following command:
193193

194194
::
195195

196-
twine upload dist/pystackql-3.1.1.tar.gz
196+
twine upload dist/pystackql-3.1.2.tar.gz

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
# The short X.Y version
2727
version = ''
2828
# The full version, including alpha/beta/rc tags
29-
release = '3.1.1'
29+
release = '3.1.2'
3030

3131

3232
# -- General configuration ---------------------------------------------------

pystackql/stackql.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from psycopg2.extras import RealDictCursor
1313
import pandas as pd
1414

15+
from io import StringIO
16+
1517
class StackQL:
1618
"""
1719
A class representing an instance of the StackQL query engine.
@@ -22,7 +24,7 @@ class StackQL:
2224
2325
server_address: The address of the StackQL server (server_mode only).
2426
:type server_address: str
25-
:default: '0.0.0.0'
27+
:default: '127.0.0.1'
2628
2729
server_port: The port of the StackQL server (server_mode only).
2830
:type server_port: int
@@ -214,7 +216,7 @@ def _run_query(self, query, is_statement=False):
214216

215217
def __init__(self,
216218
server_mode=False,
217-
server_address='0.0.0.0',
219+
server_address='127.0.0.1',
218220
server_port=5466,
219221
download_dir=None,
220222
output='dict',
@@ -264,8 +266,6 @@ def __init__(self,
264266

265267
if self.server_mode:
266268
# server mode, connect to a server via the postgres wire protocol
267-
if this_os == 'Windows':
268-
server_address = '127.0.0.1'
269269
self.server_address = server_address
270270
self.server_port = server_port
271271
# establish the connection
@@ -455,7 +455,8 @@ def execute(self, query):
455455
result = self._run_server_query(query)
456456

457457
if self.output == 'pandas':
458-
return pd.DataFrame(result) # Convert dict results to DataFrame
458+
json_str = json.dumps(result)
459+
return pd.read_json(StringIO(json_str))
459460
elif self.output == 'csv':
460461
raise ValueError("CSV output is not supported in server_mode.")
461462
else: # Assume 'dict' output
@@ -468,15 +469,15 @@ def execute(self, query):
468469
return output
469470
elif self.output == 'pandas':
470471
try:
471-
json_output = json.loads(output)
472-
return pd.DataFrame(json_output)
472+
return pd.read_json(StringIO(output))
473473
except ValueError:
474474
return pd.DataFrame([{"error": "Invalid JSON output: {}".format(output.strip())}])
475475
else: # Assume 'dict' output
476476
try:
477477
return json.loads(output)
478478
except ValueError:
479479
return [{"error": "Invalid JSON output: {}".format(output.strip())}]
480+
480481
#
481482
# asnyc query support
482483
#

pystackql/stackql_magic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def run_query(self, query):
4343
:param query: StackQL query to be executed.
4444
:type query: str
4545
:return: Query results, returned as a Pandas DataFrame.
46-
:rtype: pandas.DataFrame or str
46+
:rtype: pandas.DataFrame
4747
"""
4848
return self.stackql_instance.execute(query)
4949

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
setup(
1212
name='pystackql',
13-
version='3.1.1',
13+
version='3.1.2',
1414
description='A Python interface for StackQL',
1515
long_description=readme,
1616
author='Jeffrey Aven',

tests/pystackql_async_server_tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def setUpModule():
2525
print(res)
2626
print("starting stackql server...")
2727
PyStackQLTestsBase.server_process = subprocess.Popen([PyStackQLTestsBase.stackql.bin_path, "srv", "--pgsrv.address", server_address, "--pgsrv.port", str(server_port)])
28-
time.sleep(5)
28+
time.sleep(30)
2929

3030
def tearDownModule():
3131
print("stopping stackql server...")

tests/pystackql_tests.py

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ class PyStackQLTestsBase(unittest.TestCase):
3131
def setUpModule():
3232
print("downloading stackql binary...")
3333
PyStackQLTestsBase.stackql = StackQL()
34+
# Check whether code is running in GitHub Actions
35+
is_github_actions = os.environ.get('GITHUB_ACTIONS') == 'true'
36+
37+
if not is_github_actions:
38+
# Ensure you have the latest version of stackql, only when running locally
39+
print("Running tests outside of GitHub Actions, upgrading stackql binary...")
40+
PyStackQLTestsBase.stackql.upgrade()
41+
3442
print("downloading aws provider for tests...")
3543
res = PyStackQLTestsBase.stackql.executeStmt(registry_pull_aws_query)
3644
print(res)
@@ -39,7 +47,7 @@ def setUpModule():
3947
print(res)
4048
print("starting stackql server...")
4149
PyStackQLTestsBase.server_process = subprocess.Popen([PyStackQLTestsBase.stackql.bin_path, "srv", "--pgsrv.address", server_address, "--pgsrv.port", str(server_port)])
42-
time.sleep(5)
50+
time.sleep(10)
4351

4452
def tearDownModule():
4553
print("stopping stackql server...")
@@ -161,18 +169,26 @@ def test_11_execute_with_defaults(self):
161169
result = self.stackql.execute(google_query)
162170
is_valid_dict = isinstance(result, list) and all(isinstance(item, dict) for item in result)
163171
self.assertTrue(is_valid_dict, f"Result is not a valid dict: {result}")
164-
print_test_result(f"Test execute with defaults\nRESULT_COUNT: {len(result)}", is_valid_dict)
172+
print_test_result(f"Test execute with defaults\nRESULT: {result}", is_valid_dict)
165173

166174
@pystackql_test_setup(output='pandas')
167175
def test_12_execute_with_pandas_output(self):
168-
result = self.stackql.execute(google_query)
176+
result = self.stackql.execute(aws_query)
169177
is_valid_dataframe = isinstance(result, pd.DataFrame)
170178
self.assertTrue(is_valid_dataframe, f"Result is not a valid DataFrame: {result}")
171-
print_test_result(f"Test execute with pandas output\nRESULT_COUNT: {len(result)}", is_valid_dataframe)
179+
# Check datatypes of the columns
180+
expected_dtypes = {
181+
'instance_state': 'object', # This should be 'object' for older Pandas versions
182+
'num_instances': 'int64'
183+
}
184+
for col, expected_dtype in expected_dtypes.items():
185+
actual_dtype = result[col].dtype
186+
self.assertEqual(actual_dtype, expected_dtype, f"Column '{col}' has dtype '{actual_dtype}' but expected '{expected_dtype}'")
187+
print_test_result(f"Test execute with pandas output\nRESULT COUNT: {len(result)}", is_valid_dataframe)
172188

173189
@pystackql_test_setup(output='csv')
174190
def test_13_execute_with_csv_output(self):
175-
result = self.stackql.execute(google_query)
191+
result = self.stackql.execute(aws_query)
176192
is_valid_csv = isinstance(result, str) and result.count("\n") >= 1 and result.count(",") >= 1
177193
self.assertTrue(is_valid_csv, f"Result is not a valid CSV: {result}")
178194
print_test_result(f"Test execute with csv output\nRESULT_COUNT: {len(result.splitlines())}", is_valid_csv)
@@ -233,9 +249,18 @@ def test_21_execute_server_mode_default_output(self):
233249

234250
@pystackql_test_setup(server_mode=True, output='pandas')
235251
def test_22_execute_server_mode_pandas_output(self):
236-
result = self.stackql.execute(google_query)
237-
is_valid_pandas_output = isinstance(result, pd.DataFrame)
238-
print_test_result(f"""Test execute in server_mode with pandas output\nRESULT_COUNT: {len(result)}""", is_valid_pandas_output, True)
252+
result = self.stackql.execute(aws_query)
253+
is_valid_dataframe = isinstance(result, pd.DataFrame)
254+
self.assertTrue(is_valid_dataframe, f"Result is not a valid DataFrame: {result}")
255+
# Check datatypes of the columns
256+
expected_dtypes = {
257+
'instance_state': 'object', # This should be 'object' for older Pandas versions
258+
'num_instances': 'int64'
259+
}
260+
for col, expected_dtype in expected_dtypes.items():
261+
actual_dtype = result[col].dtype
262+
self.assertEqual(actual_dtype, expected_dtype, f"Column '{col}' has dtype '{actual_dtype}' but expected '{expected_dtype}'")
263+
print_test_result(f"Test execute in server_mode with pandas output\nRESULT COUNT: {len(result)}", is_valid_dataframe)
239264

240265
class MockInteractiveShell:
241266
"""A mock class for IPython's InteractiveShell."""

0 commit comments

Comments
 (0)