import pytest
import tempfile
import shutil
from pathlib import Path
import sys
import os
import numpy as np
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'apexbase', 'python'))
try:
from apexbase import ApexClient, ResultView, ARROW_AVAILABLE, POLARS_AVAILABLE
except ImportError as e:
pytest.skip(f"ApexBase not available: {e}", allow_module_level=True)
try:
import pandas as pd
PANDAS_AVAILABLE = True
except ImportError:
PANDAS_AVAILABLE = False
try:
import polars as pl
POLARS_DF_AVAILABLE = True
except ImportError:
POLARS_DF_AVAILABLE = False
try:
import pyarrow as pa
PYARROW_AVAILABLE = True
except ImportError:
PYARROW_AVAILABLE = False
@pytest.mark.skipif(not PANDAS_AVAILABLE, reason="Pandas not available")
class TestPandasConversions:
def test_from_pandas_basic(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
df = pd.DataFrame({
"name": ["Alice", "Bob", "Charlie"],
"age": [25, 30, 35],
"city": ["NYC", "LA", "Chicago"]
})
returned_client = client.from_pandas(df)
assert returned_client is client
count = client.count_rows()
assert count == 3
results = client.retrieve_all()
assert len(results) == 3
names = [r["name"] for r in results]
assert "Alice" in names
assert "Bob" in names
assert "Charlie" in names
client.close()
def test_to_pandas_basic(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
test_data = [
{"name": "Alice", "age": 25, "city": "NYC"},
{"name": "Bob", "age": 30, "city": "LA"},
{"name": "Charlie", "age": 35, "city": "Chicago"},
]
client.store(test_data)
results = client.query()
df = results.to_pandas()
assert isinstance(df, pd.DataFrame)
assert len(df) == 3
assert "name" in df.columns
assert "age" in df.columns
assert "city" in df.columns
assert "_id" not in df.columns
names = df["name"].tolist()
assert "Alice" in names
assert "Bob" in names
assert "Charlie" in names
client.close()
def test_pandas_zero_copy_conversion(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
test_data = [
{"name": "Alice", "age": 25},
{"name": "Bob", "age": 30},
]
client.store(test_data)
results = client.query()
try:
df = results.to_pandas()
assert isinstance(df, pd.DataFrame)
assert len(df) == 2
except Exception as e:
print(f"Pandas zero copy: {e}")
client.close()
def test_pandas_mixed_data_types(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
df = pd.DataFrame({
"string_col": ["a", "b", "c"],
"int_col": [1, 2, 3],
"float_col": [1.1, 2.2, 3.3],
"bool_col": [True, False, True],
"datetime_col": pd.date_range("2023-01-01", periods=3),
})
client.from_pandas(df)
results = client.retrieve_all()
df_result = results.to_pandas()
assert len(df_result) == 3
assert "string_col" in df_result.columns
assert "int_col" in df_result.columns
assert "float_col" in df_result.columns
assert "bool_col" in df_result.columns
client.close()
def test_pandas_with_null_values(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
df = pd.DataFrame({
"col1": [1, 2, np.nan, 4],
"col2": ["a", "b", "c", "d"],
})
client.from_pandas(df)
results = client.retrieve_all()
df_result = results.to_pandas()
assert len(df_result) == 4
client.close()
def test_pandas_empty_dataframe(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
empty_df = pd.DataFrame()
client.from_pandas(empty_df)
count = client.count_rows()
assert count == 0
results = client.query()
df_result = results.to_pandas()
assert isinstance(df_result, pd.DataFrame)
assert len(df_result) == 0
client.close()
def test_pandas_large_dataset(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
size = 10000
df = pd.DataFrame({
"id": range(size),
"value": np.random.random(size),
"category": np.random.choice(["A", "B", "C"], size),
})
client.from_pandas(df)
count = client.count_rows()
assert count == size
results = client.retrieve_all()
df_result = results.to_pandas()
assert len(df_result) == size
client.close()
@pytest.mark.skipif(not POLARS_DF_AVAILABLE, reason="Polars not available")
class TestPolarsConversions:
def test_from_polars_basic(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
df = pl.DataFrame({
"name": ["Alice", "Bob", "Charlie"],
"age": [25, 30, 35],
"city": ["NYC", "LA", "Chicago"]
})
returned_client = client.from_polars(df)
assert returned_client is client
count = client.count_rows()
assert count == 3
results = client.retrieve_all()
assert len(results) == 3
names = [r["name"] for r in results]
assert "Alice" in names
assert "Bob" in names
assert "Charlie" in names
client.close()
def test_to_polars_basic(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
test_data = [
{"name": "Alice", "age": 25, "city": "NYC"},
{"name": "Bob", "age": 30, "city": "LA"},
{"name": "Charlie", "age": 35, "city": "Chicago"},
]
client.store(test_data)
results = client.query()
df = results.to_polars()
assert isinstance(df, pl.DataFrame)
assert len(df) == 3
assert "name" in df.columns
assert "age" in df.columns
assert "city" in df.columns
assert "_id" not in df.columns
names = df["name"].to_list()
assert "Alice" in names
assert "Bob" in names
assert "Charlie" in names
client.close()
def test_polars_mixed_data_types(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
df = pl.DataFrame({
"string_col": ["a", "b", "c"],
"int_col": pl.Series("int_col", [1, 2, 3], dtype=pl.Int64),
"float_col": pl.Series("float_col", [1.1, 2.2, 3.3], dtype=pl.Float64),
"bool_col": pl.Series("bool_col", [True, False, True], dtype=pl.Boolean),
})
client.from_polars(df)
results = client.retrieve_all()
df_result = results.to_polars()
assert len(df_result) == 3
assert "string_col" in df_result.columns
assert "int_col" in df_result.columns
assert "float_col" in df_result.columns
assert "bool_col" in df_result.columns
client.close()
def test_polars_with_null_values(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
df = pl.DataFrame({
"col1": [1, 2, 3, 4],
"col2": ["a", "b", "c", "d"],
})
try:
client.from_polars(df)
results = client.retrieve_all()
assert len(results) >= 0
except (AttributeError, TypeError) as e:
print(f"Polars null: {e}")
client.close()
def test_polars_empty_dataframe(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
empty_df = pl.DataFrame()
client.from_polars(empty_df)
count = client.count_rows()
assert count == 0
results = client.query()
df_result = results.to_polars()
assert isinstance(df_result, pl.DataFrame)
assert len(df_result) == 0
client.close()
@pytest.mark.skipif(not PYARROW_AVAILABLE, reason="PyArrow not available")
class TestPyArrowConversions:
def test_from_pyarrow_basic(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
table = pa.Table.from_pydict({
"name": ["Alice", "Bob", "Charlie"],
"age": [25, 30, 35],
"city": ["NYC", "LA", "Chicago"]
})
returned_client = client.from_pyarrow(table)
assert returned_client is client
count = client.count_rows()
assert count == 3
results = client.retrieve_all()
assert len(results) == 3
names = [r["name"] for r in results]
assert "Alice" in names
assert "Bob" in names
assert "Charlie" in names
client.close()
def test_to_arrow_basic(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
test_data = [
{"name": "Alice", "age": 25, "city": "NYC"},
{"name": "Bob", "age": 30, "city": "LA"},
{"name": "Charlie", "age": 35, "city": "Chicago"},
]
client.store(test_data)
results = client.query()
table = results.to_arrow()
assert isinstance(table, pa.Table)
assert len(table) == 3
assert "name" in table.column_names
assert "age" in table.column_names
assert "city" in table.column_names
assert "_id" not in table.column_names
names = table.column("name").to_pylist()
assert "Alice" in names
assert "Bob" in names
assert "Charlie" in names
client.close()
def test_arrow_mixed_data_types(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
table = pa.Table.from_pydict({
"string_col": ["a", "b", "c"],
"int_col": pa.array([1, 2, 3], type=pa.int64()),
"float_col": pa.array([1.1, 2.2, 3.3], type=pa.float64()),
"bool_col": pa.array([True, False, True], type=pa.bool_()),
})
client.from_pyarrow(table)
results = client.retrieve_all()
table_result = results.to_arrow()
assert len(table_result) == 3
assert "string_col" in table_result.column_names
assert "int_col" in table_result.column_names
assert "float_col" in table_result.column_names
assert "bool_col" in table_result.column_names
client.close()
def test_arrow_with_null_values(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
table = pa.Table.from_pydict({
"col1": [1, 2, 3, 4],
"col2": ["a", "b", "c", "d"],
})
client.from_pyarrow(table)
results = client.retrieve_all()
table_result = results.to_arrow()
assert len(table_result) == 4
client.close()
def test_arrow_empty_table(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
empty_table = pa.Table.from_pydict({})
client.from_pyarrow(empty_table)
count = client.count_rows()
assert count == 0
results = client.query()
table_result = results.to_arrow()
assert isinstance(table_result, pa.Table)
assert len(table_result) == 0
client.close()
@pytest.mark.skipif(not (PANDAS_AVAILABLE and POLARS_AVAILABLE), reason="Pandas and Polars not available")
class TestCrossFormatConversions:
def test_polars_to_pandas_via_apexbase(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
pl_df = pl.DataFrame({
"name": ["Alice", "Bob", "Charlie"],
"age": [25, 30, 35],
"city": ["NYC", "LA", "Chicago"]
})
client.from_polars(pl_df)
results = client.retrieve_all()
pd_df = results.to_pandas()
assert len(pd_df) == 3
assert list(pd_df["name"]) == ["Alice", "Bob", "Charlie"]
assert list(pd_df["age"]) == [25, 30, 35]
client.close()
def test_pandas_to_polars_via_apexbase(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
pd_df = pd.DataFrame({
"name": ["Alice", "Bob", "Charlie"],
"age": [25, 30, 35],
"city": ["NYC", "LA", "Chicago"]
})
client.from_pandas(pd_df)
results = client.retrieve_all()
pl_df = results.to_polars()
assert len(pl_df) == 3
assert pl_df["name"].to_list() == ["Alice", "Bob", "Charlie"]
assert pl_df["age"].to_list() == [25, 30, 35]
client.close()
@pytest.mark.skipif(not (PANDAS_AVAILABLE and PYARROW_AVAILABLE), reason="Pandas and PyArrow not available")
class TestArrowPandasIntegration:
def test_arrow_pandas_roundtrip(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
original_table = pa.Table.from_pydict({
"name": ["Alice", "Bob", "Charlie"],
"age": [25, 30, 35],
})
client.from_pyarrow(original_table)
results = client.retrieve_all()
df = results.to_pandas()
assert len(df) == 3
client.close()
class TestConversionEdgeCases:
@pytest.mark.skipif(not PANDAS_AVAILABLE, reason="Pandas not available")
def test_pandas_conversion_errors(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
empty_results = client.query()
df = empty_results.to_pandas()
assert isinstance(df, pd.DataFrame)
assert len(df) == 0
with pytest.raises(Exception):
client.from_pandas("not a dataframe")
client.close()
@pytest.mark.skipif(not POLARS_DF_AVAILABLE, reason="Polars not available")
def test_polars_conversion_errors(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
empty_results = client.query()
df = empty_results.to_polars()
assert isinstance(df, pl.DataFrame)
assert len(df) == 0
with pytest.raises(Exception):
client.from_polars("not a dataframe")
client.close()
@pytest.mark.skipif(not PYARROW_AVAILABLE, reason="PyArrow not available")
def test_arrow_conversion_errors(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
empty_results = client.query()
table = empty_results.to_arrow()
assert isinstance(table, pa.Table)
assert len(table) == 0
with pytest.raises(Exception):
client.from_pyarrow("not a table")
client.close()
@pytest.mark.skipif(not PANDAS_AVAILABLE, reason="Pandas not available")
def test_conversion_with_special_characters(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
df = pd.DataFrame({
"quotes": 'Single "double" quotes',
"newlines": "Line 1\nLine 2",
"tabs": "Tab\tseparated",
"unicode": "Unicode: ñáéÃóú",
"emoji": "Emoji: 🎉🚀",
}, index=[0])
client.from_pandas(df)
results = client.retrieve_all()
df_result = results.to_pandas()
assert df_result["quotes"].iloc[0] == df["quotes"].iloc[0]
assert df_result["newlines"].iloc[0] == df["newlines"].iloc[0]
assert df_result["tabs"].iloc[0] == df["tabs"].iloc[0]
assert df_result["unicode"].iloc[0] == df["unicode"].iloc[0]
assert df_result["emoji"].iloc[0] == df["emoji"].iloc[0]
client.close()
class TestConversionPerformance:
@pytest.mark.skipif(not PANDAS_AVAILABLE, reason="Pandas not available")
def test_pandas_conversion_performance(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
import time
size = 10000
df = pd.DataFrame({
"id": range(size),
"value": np.random.random(size),
"category": np.random.choice(["A", "B", "C"], size),
})
start_time = time.time()
client.from_pandas(df)
from_time = time.time() - start_time
start_time = time.time()
results = client.retrieve_all()
df_result = results.to_pandas()
to_time = time.time() - start_time
assert from_time < 5.0
assert to_time < 5.0
assert len(df_result) == size
client.close()
@pytest.mark.skipif(not POLARS_DF_AVAILABLE, reason="Polars not available")
def test_polars_conversion_performance(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
import time
size = 10000
df = pl.DataFrame({
"id": range(size),
"value": np.random.random(size),
"category": np.random.choice(["A", "B", "C"], size),
})
start_time = time.time()
client.from_polars(df)
from_time = time.time() - start_time
start_time = time.time()
results = client.retrieve_all()
df_result = results.to_polars()
to_time = time.time() - start_time
assert from_time < 5.0
assert to_time < 5.0
assert len(df_result) == size
client.close()
class TestSqlResultConversions:
@pytest.mark.skipif(not PANDAS_AVAILABLE, reason="Pandas not available")
def test_sql_result_to_pandas(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
test_data = [
{"name": "Alice", "age": 25, "city": "NYC"},
{"name": "Bob", "age": 30, "city": "LA"},
]
client.store(test_data)
result = client.execute("SELECT name, age FROM default ORDER BY age")
df = result.to_pandas()
assert isinstance(df, pd.DataFrame)
assert len(df) == 2
assert "name" in df.columns
assert "age" in df.columns
assert "_id" not in df.columns
names = df["name"].tolist()
ages = df["age"].tolist()
assert names == ["Alice", "Bob"] assert ages == [25, 30]
client.close()
@pytest.mark.skipif(not POLARS_DF_AVAILABLE, reason="Polars not available")
def test_sql_result_to_polars(self):
with tempfile.TemporaryDirectory() as temp_dir:
client = ApexClient(dirpath=temp_dir)
client.create_table("default")
test_data = [
{"name": "Alice", "age": 25, "city": "NYC"},
{"name": "Bob", "age": 30, "city": "LA"},
]
client.store(test_data)
result = client.execute("SELECT name, age FROM default ORDER BY age")
df = result.to_polars()
assert isinstance(df, pl.DataFrame)
assert len(df) == 2
assert "name" in df.columns
assert "age" in df.columns
assert "_id" not in df.columns
names = df["name"].to_list()
ages = df["age"].to_list()
assert names == ["Alice", "Bob"] assert ages == [25, 30]
client.close()
if __name__ == "__main__":
pytest.main([__file__, "-v"])