Skip to content

Commit ce08d11

Browse files
Antony BaileyAntony Bailey
authored andcommitted
tests finally passing
1 parent 18548c7 commit ce08d11

File tree

19 files changed

+316
-35
lines changed

19 files changed

+316
-35
lines changed

.github/workflows/python-package.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@ jobs:
3636
black --check .
3737
- name: Test with pytest
3838
run: |
39-
pytest
39+
./run-tests.sh

run_tests.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/bash
2+
# Simple script to run tests for pySQLY
3+
4+
# Exit on any error
5+
set -e
6+
7+
echo "Installing dependencies for testing..."
8+
python -m pip install -e ".[dev]"
9+
10+
echo "Running tests..."
11+
pytest
12+
13+
echo "Tests completed successfully!"

setup.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""Setup script for pySQLY."""
2+
3+
from setuptools import find_packages, setup
4+
5+
if __name__ == "__main__":
6+
setup(
7+
name="pysqly",
8+
version="0.1.0",
9+
description="SQL with YAML - A simplified query language for "
10+
"multiple databases",
11+
long_description=open("README.md").read(),
12+
long_description_content_type="text/markdown",
13+
author="Standard Query Language",
14+
author_email="example@example.com",
15+
url="https://github.com/Standard-Query-Language/pySQLY",
16+
packages=find_packages(where="src"),
17+
package_dir={"": "src"},
18+
include_package_data=True,
19+
install_requires=[
20+
"pyyaml>=6.0",
21+
],
22+
extras_require={
23+
"mariadb": ["mysql-connector-python>=8.0"],
24+
"postgres": ["psycopg2>=2.9"],
25+
"oracle": ["cx_Oracle>=8.0"],
26+
"mssql": ["pyodbc>=4.0"],
27+
"all": [
28+
"mysql-connector-python>=8.0",
29+
"psycopg2>=2.9",
30+
"cx_Oracle>=8.0",
31+
"pyodbc>=4.0",
32+
],
33+
"dev": [
34+
"pytest>=7.0",
35+
"black>=23.0",
36+
"isort>=5.0",
37+
"ruff>=0.0.1",
38+
"pre-commit>=3.0",
39+
],
40+
},
41+
classifiers=[
42+
"Development Status :: 4 - Beta",
43+
"Intended Audience :: Developers",
44+
"License :: OSI Approved :: MIT License",
45+
"Programming Language :: Python :: 3",
46+
"Programming Language :: Python :: 3.9",
47+
"Programming Language :: Python :: 3.10",
48+
"Programming Language :: Python :: 3.11",
49+
"Topic :: Database",
50+
"Topic :: Software Development :: Libraries :: Python Modules",
51+
],
52+
python_requires=">=3.9",
53+
entry_points={
54+
"console_scripts": [
55+
"sqly-cli=pysqly.cli:main",
56+
],
57+
},
58+
)

src/pysqly/connectors/base.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
from typing import Any, Dict, List
44

5-
from pysqly.core import SQLYUtils
65
from pysqly.errors import SQLYExecutionError
76

7+
# Import SQLYUtils when needed to avoid circular imports
8+
# from pysqly.core import SQLYUtils
89
from .interface import IDBConnector
910

1011

@@ -35,9 +36,35 @@ def execute_query(self, query: Dict[str, Any]) -> Any:
3536
Returns:
3637
The result of the executed query.
3738
"""
38-
sql, params = SQLYUtils.translate_to_sql(query)
39+
# Import here to avoid circular imports
40+
from pysqly.core import SQLYUtils
41+
42+
sql, params = SQLYUtils.translate_to_sql(query, self.get_db_type())
3943
return self.execute(sql, params)
4044

45+
def get_db_type(self) -> str:
46+
"""
47+
Get the database type for this connector.
48+
49+
Returns:
50+
String representing the database type
51+
"""
52+
# Extract the database type from the class name
53+
# (e.g., SQLiteConnector -> sqlite)
54+
class_name = self.__class__.__name__.lower()
55+
if "sqlite" in class_name:
56+
return "sqlite"
57+
elif "mariadb" in class_name or "mysql" in class_name:
58+
return "mariadb"
59+
elif "postgres" in class_name:
60+
return "postgres"
61+
elif "oracle" in class_name:
62+
return "oracle"
63+
elif "mssql" in class_name:
64+
return "mssql"
65+
else:
66+
raise ValueError(f"Unknown database type for {class_name}")
67+
4168
def execute(self, sql: str, params: List[Any]) -> Any:
4269
"""
4370
Execute a given SQL statement with the provided parameters.

src/pysqly/connectors/database.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
from typing import Any, Dict, Optional
44

5-
from .factory import DBConnectorFactory
5+
# Import factory at time of use to avoid circular imports
6+
# from .factory import DBConnectorFactory
67

78

89
class DatabaseConnector:
@@ -51,6 +52,9 @@ def _ensure_connector(self) -> Any:
5152
The database connector instance.
5253
"""
5354
if self.connector is None:
55+
# Import here to avoid circular imports
56+
from .factory import DBConnectorFactory
57+
5458
self.connector = DBConnectorFactory.create_connector(
5559
self.db_type, self.connection
5660
)
@@ -83,6 +87,9 @@ def execute_query(
8387
connection = connection or self.connection
8488

8589
if db_type != self.db_type or connection != self.connection:
90+
# Import here to avoid circular imports
91+
from .factory import DBConnectorFactory
92+
8693
# If parameters differ from instance attributes, create a new connector
8794
connector = DBConnectorFactory.create_connector(db_type, connection)
8895
else:

src/pysqly/connectors/factory.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44

55
from pysqly.errors import SQLYExecutionError
66

7-
from .mariadb import MariaDBConnector
8-
from .mssql import MSSQLConnector
9-
from .oracle import OracleConnector
10-
from .postgres import PostgresConnector
7+
# Import connectors directly instead of from module to avoid circular dependencies
8+
from .mariadb import MYSQL_AVAILABLE, MariaDBConnector
9+
from .mssql import MSSQL_AVAILABLE, MSSQLConnector
10+
from .oracle import ORACLE_AVAILABLE, OracleConnector
11+
from .postgres import POSTGRES_AVAILABLE, PostgresConnector
1112
from .sqlite import SQLiteConnector
1213

1314

@@ -34,15 +35,41 @@ def create_connector(db_type: str, connection: Any) -> Any:
3435
An instance of the corresponding database connector class.
3536
3637
Raises:
37-
SQLYExecutionError: If the specified database type is not supported.
38+
SQLYExecutionError: If the specified database type is not supported or
39+
if the required database driver is not installed.
3840
"""
41+
# Check if the requested database type is available
42+
if db_type == "mariadb" and not MYSQL_AVAILABLE:
43+
raise SQLYExecutionError(
44+
"MariaDB/MySQL connector is not available. "
45+
"Please install mysql-connector-python package."
46+
)
47+
elif db_type == "postgres" and not POSTGRES_AVAILABLE:
48+
raise SQLYExecutionError(
49+
"PostgreSQL connector is not available. "
50+
"Please install psycopg2 package."
51+
)
52+
elif db_type == "oracle" and not ORACLE_AVAILABLE:
53+
raise SQLYExecutionError(
54+
"Oracle connector is not available. "
55+
"Please install cx_Oracle package."
56+
)
57+
elif db_type == "mssql" and not MSSQL_AVAILABLE:
58+
raise SQLYExecutionError(
59+
"MS SQL Server connector is not available. "
60+
"Please install pyodbc package."
61+
)
62+
63+
# Define the mapping of database types to connector classes
3964
connectors = {
4065
"sqlite": SQLiteConnector,
4166
"mariadb": MariaDBConnector,
4267
"postgres": PostgresConnector,
4368
"oracle": OracleConnector,
4469
"mssql": MSSQLConnector,
4570
}
71+
4672
if db_type not in connectors:
4773
raise SQLYExecutionError(f"Unsupported database type: {db_type}")
74+
4875
return connectors[db_type](connection)

src/pysqly/connectors/mariadb.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22

33
from typing import Any, Union
44

5-
import mysql.connector
5+
# Import MySQL connector conditionally
6+
try:
7+
import mysql.connector
8+
9+
MYSQL_AVAILABLE = True
10+
except ImportError:
11+
MYSQL_AVAILABLE = False
612

713
from .base import BaseDBConnector
814

@@ -25,7 +31,17 @@ def __init__(self, connection: Union[str, Any]) -> None:
2531
connection: If a string is provided, it's treated as a connection string
2632
and a new connection is established. If a connection object is provided,
2733
it's used directly.
34+
35+
Raises:
36+
ImportError: If the mysql-connector-python package is not installed.
2837
"""
38+
if not MYSQL_AVAILABLE:
39+
raise ImportError(
40+
"mysql-connector-python is not installed. "
41+
"Please install it with 'pip install mysql-connector-python' "
42+
"or 'pip install pysqly[mariadb]'."
43+
)
44+
2945
if isinstance(connection, str):
3046
connection = mysql.connector.connect(connection)
3147
super().__init__(connection)

src/pysqly/connectors/mssql.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22

33
from typing import Any, Union
44

5-
import pyodbc
5+
# Import MS SQL connector conditionally
6+
try:
7+
import pyodbc
8+
9+
MSSQL_AVAILABLE = True
10+
except ImportError:
11+
MSSQL_AVAILABLE = False
612

713
from .base import BaseDBConnector
814

@@ -27,7 +33,17 @@ def __init__(self, connection: Union[str, Any]) -> None:
2733
If a connection string is provided, it should be in the format required by
2834
pyodbc, typically including server, database, authentication details,
2935
and other relevant parameters.
36+
37+
Raises:
38+
ImportError: If the pyodbc package is not installed.
3039
"""
40+
if not MSSQL_AVAILABLE:
41+
raise ImportError(
42+
"pyodbc is not installed. "
43+
"Please install it with 'pip install pyodbc' "
44+
"or 'pip install pysqly[mssql]'."
45+
)
46+
3147
if isinstance(connection, str):
3248
connection = pyodbc.connect(connection)
3349
super().__init__(connection)

src/pysqly/connectors/oracle.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22

33
from typing import Any, Union
44

5-
import cx_Oracle
5+
# Import Oracle connector conditionally
6+
try:
7+
import cx_Oracle
8+
9+
ORACLE_AVAILABLE = True
10+
except ImportError:
11+
ORACLE_AVAILABLE = False
612

713
from .base import BaseDBConnector
814

@@ -29,7 +35,17 @@ def __init__(self, connection: Union[str, Any]) -> None:
2935
Notes:
3036
The connection string format follows the Oracle standard:
3137
"username/password@host:port/service_name"
38+
39+
Raises:
40+
ImportError: If the cx_Oracle package is not installed.
3241
"""
42+
if not ORACLE_AVAILABLE:
43+
raise ImportError(
44+
"cx_Oracle is not installed. "
45+
"Please install it with 'pip install cx_Oracle' "
46+
"or 'pip install pysqly[oracle]'."
47+
)
48+
3349
if isinstance(connection, str):
3450
connection = cx_Oracle.connect(connection)
3551
super().__init__(connection)

src/pysqly/connectors/postgres.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22

33
from typing import Any, Union
44

5-
import psycopg2
5+
# Import PostgreSQL connector conditionally
6+
try:
7+
import psycopg2
8+
9+
POSTGRES_AVAILABLE = True
10+
except ImportError:
11+
POSTGRES_AVAILABLE = False
612

713
from .base import BaseDBConnector
814

@@ -26,7 +32,17 @@ def __init__(self, connection: Union[str, Any]) -> None:
2632
Notes:
2733
The connection string should be in the format:
2834
"host=hostname dbname=database user=username password=password"
35+
36+
Raises:
37+
ImportError: If the psycopg2 package is not installed.
2938
"""
39+
if not POSTGRES_AVAILABLE:
40+
raise ImportError(
41+
"psycopg2 is not installed. "
42+
"Please install it with 'pip install psycopg2' "
43+
"or 'pip install pysqly[postgres]'."
44+
)
45+
3046
if isinstance(connection, str):
3147
connection = psycopg2.connect(connection)
3248
super().__init__(connection)

0 commit comments

Comments
 (0)