excel-dbapi 0.1.4__tar.gz → 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. excel_dbapi-2.0.0/PKG-INFO +22 -0
  2. excel_dbapi-2.0.0/README.md +92 -0
  3. excel_dbapi-2.0.0/pyproject.toml +42 -0
  4. excel_dbapi-2.0.0/setup.cfg +4 -0
  5. excel_dbapi-2.0.0/src/excel_dbapi/connection.py +107 -0
  6. excel_dbapi-2.0.0/src/excel_dbapi/cursor.py +84 -0
  7. excel_dbapi-2.0.0/src/excel_dbapi/engine/__init__.py +5 -0
  8. excel_dbapi-2.0.0/src/excel_dbapi/engine/base.py +18 -0
  9. excel_dbapi-2.0.0/src/excel_dbapi/engine/executor.py +40 -0
  10. excel_dbapi-2.0.0/src/excel_dbapi/engine/openpyxl_engine.py +39 -0
  11. excel_dbapi-2.0.0/src/excel_dbapi/engine/pandas_engine.py +49 -0
  12. excel_dbapi-2.0.0/src/excel_dbapi/engine/parser.py +49 -0
  13. excel_dbapi-2.0.0/src/excel_dbapi/exceptions.py +52 -0
  14. excel_dbapi-2.0.0/src/excel_dbapi.egg-info/PKG-INFO +22 -0
  15. excel_dbapi-2.0.0/src/excel_dbapi.egg-info/SOURCES.txt +25 -0
  16. excel_dbapi-2.0.0/src/excel_dbapi.egg-info/dependency_links.txt +1 -0
  17. excel_dbapi-2.0.0/src/excel_dbapi.egg-info/requires.txt +15 -0
  18. excel_dbapi-2.0.0/src/excel_dbapi.egg-info/top_level.txt +1 -0
  19. excel_dbapi-2.0.0/tests/test_connection.py +71 -0
  20. excel_dbapi-2.0.0/tests/test_cursor.py +23 -0
  21. excel_dbapi-2.0.0/tests/test_engine.py +55 -0
  22. excel_dbapi-2.0.0/tests/test_exceptions.py +17 -0
  23. excel_dbapi-2.0.0/tests/test_executor.py +27 -0
  24. excel_dbapi-2.0.0/tests/test_integration.py +1 -0
  25. excel_dbapi-2.0.0/tests/test_parser.py +53 -0
  26. excel_dbapi-0.1.4/.gitignore +0 -91
  27. excel_dbapi-0.1.4/PKG-INFO +0 -21
  28. excel_dbapi-0.1.4/excel_dbapi/__init__.py +0 -3
  29. excel_dbapi-0.1.4/excel_dbapi/api.py +0 -9
  30. excel_dbapi-0.1.4/excel_dbapi/connection.py +0 -75
  31. excel_dbapi-0.1.4/excel_dbapi/cursor.py +0 -75
  32. excel_dbapi-0.1.4/excel_dbapi/engines/openpyxl_engine.py +0 -46
  33. excel_dbapi-0.1.4/excel_dbapi/exceptions.py +0 -34
  34. excel_dbapi-0.1.4/excel_dbapi/query.py +0 -54
  35. excel_dbapi-0.1.4/excel_dbapi/remote.py +0 -17
  36. excel_dbapi-0.1.4/excel_dbapi/table.py +0 -53
  37. excel_dbapi-0.1.4/pyproject.toml +0 -26
  38. {excel_dbapi-0.1.4 → excel_dbapi-2.0.0}/LICENSE +0 -0
  39. {excel_dbapi-0.1.4/excel_dbapi/engines → excel_dbapi-2.0.0/src/excel_dbapi}/__init__.py +0 -0
@@ -0,0 +1,22 @@
1
+ Metadata-Version: 2.4
2
+ Name: excel-dbapi
3
+ Version: 2.0.0
4
+ Summary: PEP 249 compliant DB-API driver for Excel files
5
+ Author-email: Yeongseon Choe <yeongseon.choe@gmail.com>
6
+ Requires-Python: >=3.9
7
+ License-File: LICENSE
8
+ Requires-Dist: pandas<3.0.0,>=2.0.0
9
+ Requires-Dist: openpyxl<4.0.0,>=3.1.0
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest<9.0.0,>=8.3.5; extra == "dev"
12
+ Requires-Dist: pytest-cov<5.0.0,>=4.1; extra == "dev"
13
+ Requires-Dist: black<26.0.0,>=25.1.0; extra == "dev"
14
+ Requires-Dist: isort<7.0.0,>=6.0.1; extra == "dev"
15
+ Requires-Dist: ruff<0.12.0,>=0.11.2; extra == "dev"
16
+ Requires-Dist: mypy<2.0.0,>=1.15.0; extra == "dev"
17
+ Requires-Dist: build; extra == "dev"
18
+ Requires-Dist: bandit<2.0.0,>=1.7.0; extra == "dev"
19
+ Requires-Dist: vulture<3.0,>=2.0; extra == "dev"
20
+ Requires-Dist: pre-commit; extra == "dev"
21
+ Requires-Dist: types-requests<3.0.0,>=2.28.0; extra == "dev"
22
+ Dynamic: license-file
@@ -0,0 +1,92 @@
1
+
2
+ # excel-dbapi
3
+
4
+ ![CI](https://github.com/your-username/excel-dbapi/actions/workflows/ci.yml/badge.svg)
5
+ [![codecov](https://codecov.io/gh/your-username/excel-dbapi/branch/main/graph/badge.svg)](https://codecov.io/gh/your-username/excel-dbapi)
6
+
7
+ A lightweight, Python DB-API 2.0 compliant connector for Excel files.
8
+
9
+ ---
10
+
11
+ ## Features
12
+
13
+ - Python DB-API 2.0 compliant interface
14
+ - Query Excel files using SQL syntax
15
+ - Supports SELECT (INSERT, UPDATE, DELETE planned)
16
+ - Sheet-to-Table mapping
17
+ - Pandas & Openpyxl engine selector
18
+ - Transaction simulation (planned)
19
+ - SQLAlchemy Dialect integration (planned)
20
+
21
+ ---
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pip install excel-dbapi
27
+ ```
28
+
29
+ See [CHANGELOG](CHANGELOG.md) for release history.
30
+
31
+ ---
32
+
33
+ ## Quick Start
34
+
35
+ ### Basic Usage (Local File)
36
+
37
+ ```python
38
+ from excel_dbapi.connection import ExcelConnection
39
+
40
+ # Using default engine (openpyxl)
41
+ with ExcelConnection("path/to/sample.xlsx") as conn:
42
+ cursor = conn.cursor()
43
+ cursor.execute("SELECT * FROM Sheet1")
44
+ print(cursor.fetchall())
45
+
46
+ # Using pandas engine
47
+ with ExcelConnection("path/to/sample.xlsx", engine="pandas") as conn:
48
+ cursor = conn.cursor()
49
+ cursor.execute("SELECT * FROM Sheet1")
50
+ print(cursor.fetchall())
51
+ ```
52
+
53
+ ### Engine Options
54
+
55
+ | Engine | Description | Dependency |
56
+ |---------|------------------------------|--------------|
57
+ | openpyxl (default) | Fast sheet access (read-only) | openpyxl |
58
+ | pandas | DataFrame based operations | pandas, openpyxl |
59
+
60
+ You can explicitly specify the engine using:
61
+
62
+ ```python
63
+ conn = ExcelConnection("sample.xlsx", engine="openpyxl")
64
+ conn = ExcelConnection("sample.xlsx", engine="pandas")
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Planned Features
70
+
71
+ - Write operations (INSERT, UPDATE, DELETE)
72
+ - DDL support (CREATE TABLE, DROP TABLE)
73
+ - Transaction simulation
74
+ - Advanced SQL condition support (WHERE, ORDER BY, LIMIT)
75
+ - Remote file connection support
76
+ - SQLAlchemy Dialect
77
+
78
+ See [Project Roadmap](docs/ROADMAP.md) for details.
79
+
80
+ ---
81
+
82
+ ## Documentation
83
+
84
+ - [Usage Guide](docs/USAGE.md)
85
+ - [Development Guide](docs/DEVELOPMENT.md)
86
+ - [Project Roadmap](docs/ROADMAP.md)
87
+
88
+ ---
89
+
90
+ ## License
91
+
92
+ MIT License
@@ -0,0 +1,42 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "excel-dbapi"
7
+ version = "2.0.0"
8
+ description = "PEP 249 compliant DB-API driver for Excel files"
9
+ requires-python = ">=3.9"
10
+ dependencies = [
11
+ "pandas>=2.0.0,<3.0.0",
12
+ "openpyxl>=3.1.0,<4.0.0"
13
+ ]
14
+
15
+ [[project.authors]]
16
+ name = "Yeongseon Choe"
17
+ email = "yeongseon.choe@gmail.com"
18
+
19
+ [project.optional-dependencies]
20
+ dev = [
21
+ "pytest>=8.3.5,<9.0.0",
22
+ "pytest-cov>=4.1,<5.0.0",
23
+ "black>=25.1.0,<26.0.0",
24
+ "isort>=6.0.1,<7.0.0",
25
+ "ruff>=0.11.2,<0.12.0",
26
+ "mypy>=1.15.0,<2.0.0",
27
+ "build",
28
+ "bandit>=1.7.0,<2.0.0",
29
+ "vulture>=2.0,<3.0",
30
+ "pre-commit",
31
+ "types-requests>=2.28.0,<3.0.0"
32
+ ]
33
+
34
+ [tool.mypy]
35
+ ignore_missing_imports = true
36
+ strict = false
37
+
38
+ [tool.setuptools.packages.find]
39
+ where = ["src"]
40
+
41
+ [tool.pytest.ini_options]
42
+ pythonpath = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,107 @@
1
+ from typing import Optional, Type
2
+
3
+ from .cursor import ExcelCursor
4
+ from .engine import BaseEngine, OpenpyxlEngine, PandasEngine
5
+ from .exceptions import InterfaceError, NotSupportedError
6
+
7
+
8
+ def check_closed(func):
9
+ """Decorator to check if connection is closed before executing method."""
10
+
11
+ def wrapper(self, *args, **kwargs):
12
+ if self.closed:
13
+ raise InterfaceError("Connection is already closed")
14
+ return func(self, *args, **kwargs)
15
+
16
+ return wrapper
17
+
18
+
19
+ class ExcelConnection:
20
+ """
21
+ ExcelConnection provides a PEP 249 compliant Connection interface
22
+ for reading and querying Excel files.
23
+ """
24
+
25
+ def __init__(self, file_path: str, engine: str = "openpyxl"):
26
+ """
27
+ Initialize the connection with the Excel file and selected engine.
28
+
29
+ Args:
30
+ file_path (str): Path to the Excel file.
31
+ engine (str): Engine type ('pandas' or 'openpyxl').
32
+ """
33
+ self.file_path: str = file_path
34
+ self.closed: bool = False
35
+
36
+ if engine == "pandas":
37
+ self.engine: BaseEngine = PandasEngine(file_path)
38
+ elif engine == "openpyxl":
39
+ self.engine: BaseEngine = OpenpyxlEngine(file_path)
40
+ else:
41
+ raise ValueError(f"Unsupported engine: {engine}")
42
+
43
+ @check_closed
44
+ def cursor(self) -> ExcelCursor:
45
+ """
46
+ Return a new Cursor object using the connection.
47
+
48
+ Returns:
49
+ ExcelCursor: A new cursor object.
50
+ """
51
+ return ExcelCursor(self.engine)
52
+
53
+ @check_closed
54
+ def commit(self) -> None:
55
+ """
56
+ Commit any pending transaction (Not supported for Excel).
57
+
58
+ Raises:
59
+ NotSupportedError: Always raised because transactions are not supported.
60
+ """
61
+ raise NotSupportedError("Transactions are not supported")
62
+
63
+ @check_closed
64
+ def rollback(self) -> None:
65
+ """
66
+ Roll back to the start of any pending transaction (Not supported for Excel).
67
+
68
+ Raises:
69
+ NotSupportedError: Always raised because transactions are not supported.
70
+ """
71
+ raise NotSupportedError("Transactions are not supported")
72
+
73
+ def close(self) -> None:
74
+ """
75
+ Close the connection.
76
+ """
77
+ self.closed = True
78
+
79
+ @property
80
+ def engine_name(self) -> str:
81
+ """
82
+ Return the name of the engine being used.
83
+ """
84
+ return self.engine.__class__.__name__
85
+
86
+ def __str__(self) -> str:
87
+ return f"<ExcelConnection file='{self.file_path}' engine='{self.engine_name}' closed={self.closed}>"
88
+
89
+ def __repr__(self) -> str:
90
+ return self.__str__()
91
+
92
+ def __enter__(self) -> "ExcelConnection":
93
+ """
94
+ Enter the runtime context related to this object.
95
+ """
96
+ return self
97
+
98
+ def __exit__(
99
+ self,
100
+ exc_type: Optional[Type[BaseException]],
101
+ exc_val: Optional[BaseException],
102
+ exc_tb,
103
+ ) -> None:
104
+ """
105
+ Exit the runtime context and close the connection.
106
+ """
107
+ self.close()
@@ -0,0 +1,84 @@
1
+ from typing import Any, Dict, List, Optional
2
+
3
+ from .engine.executor import execute_query
4
+ from .engine.parser import parse_sql
5
+ from .exceptions import InterfaceError
6
+
7
+
8
+ def check_closed(func):
9
+ """Decorator to check if cursor is closed before executing method."""
10
+
11
+ def wrapper(self, *args, **kwargs):
12
+ if self.closed:
13
+ raise InterfaceError("Cursor is already closed")
14
+ return func(self, *args, **kwargs)
15
+
16
+ return wrapper
17
+
18
+
19
+ class ExcelCursor:
20
+ """
21
+ ExcelCursor provides a PEP 249 compliant Cursor interface
22
+ for executing SQL-like queries on Excel data.
23
+ """
24
+
25
+ def __init__(self, connection: Any):
26
+ """
27
+ Initialize the cursor with a connection.
28
+
29
+ Args:
30
+ connection (ExcelConnection): The parent connection object.
31
+ """
32
+ self.connection = connection
33
+ self.closed: bool = False
34
+ self._results: List[Dict[str, Any]] = []
35
+ self._index: int = 0
36
+
37
+ @check_closed
38
+ def execute(self, query: str, params: Optional[tuple] = None) -> "ExcelCursor":
39
+ """
40
+ Execute a SQL query.
41
+
42
+ Args:
43
+ query (str): The SQL query string.
44
+ params (Optional[tuple]): Parameters to bind to query placeholders.
45
+
46
+ Returns:
47
+ ExcelCursor: The cursor itself.
48
+ """
49
+ parsed = parse_sql(query, params)
50
+ self._results = execute_query(parsed, self.connection.data)
51
+ self._index = 0
52
+ return self
53
+
54
+ @check_closed
55
+ def fetchone(self) -> Optional[Dict[str, Any]]:
56
+ """
57
+ Fetch the next row of a query result.
58
+
59
+ Returns:
60
+ Optional[Dict[str, Any]]: The next row or None if no more rows.
61
+ """
62
+ if self._index >= len(self._results):
63
+ return None
64
+ result = self._results[self._index]
65
+ self._index += 1
66
+ return result
67
+
68
+ @check_closed
69
+ def fetchall(self) -> List[Dict[str, Any]]:
70
+ """
71
+ Fetch all remaining rows of a query result.
72
+
73
+ Returns:
74
+ List[Dict[str, Any]]: List of all remaining rows.
75
+ """
76
+ results = self._results[self._index :]
77
+ self._index = len(self._results)
78
+ return results
79
+
80
+ def close(self) -> None:
81
+ """
82
+ Close the cursor.
83
+ """
84
+ self.closed = True
@@ -0,0 +1,5 @@
1
+ from .base import BaseEngine
2
+ from .openpyxl_engine import OpenpyxlEngine
3
+ from .pandas_engine import PandasEngine
4
+
5
+ __all__ = ["BaseEngine", "PandasEngine", "OpenpyxlEngine"]
@@ -0,0 +1,18 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any, Dict, List
3
+
4
+
5
+ class BaseEngine(ABC):
6
+ @abstractmethod
7
+ def load(self) -> Dict[str, Any]:
8
+ """
9
+ Load data from the Excel file.
10
+ """
11
+ pass
12
+
13
+ @abstractmethod
14
+ def execute(self, query: str) -> List[Dict[str, Any]]:
15
+ """
16
+ Execute a query against the loaded data.
17
+ """
18
+ pass
@@ -0,0 +1,40 @@
1
+ from typing import Any, Dict, List
2
+
3
+ from pandas import DataFrame
4
+
5
+
6
+ def execute_query(
7
+ parsed: Dict[str, Any], data: Dict[str, DataFrame]
8
+ ) -> List[Dict[str, Any]]:
9
+ """
10
+ Execute the parsed SQL query against the provided Excel data.
11
+
12
+ Args:
13
+ parsed (Dict[str, Any]): Parsed SQL query components.
14
+ data (Dict[str, DataFrame]): Excel sheet data as DataFrames.
15
+
16
+ Returns:
17
+ List[Dict[str, Any]]: Query result as list of dictionaries.
18
+
19
+ Raises:
20
+ ValueError: If the specified sheet (table) is not found.
21
+ NotImplementedError: For unsupported SQL actions.
22
+ """
23
+ table = parsed.get("table")
24
+ if table not in data:
25
+ raise ValueError(f"Sheet '{table}' not found in Excel")
26
+
27
+ if parsed["action"] == "SELECT":
28
+ df = data[table]
29
+ if parsed.get("where"):
30
+ try:
31
+ df = df.query(parsed["where"])
32
+ except Exception as e:
33
+ raise ValueError(f"Invalid WHERE condition: {parsed['where']}") from e
34
+ return df.to_dict(orient="records")
35
+
36
+ elif parsed["action"] == "INSERT":
37
+ raise NotImplementedError("INSERT is not yet implemented")
38
+
39
+ else:
40
+ raise NotImplementedError(f"Unsupported action: {parsed['action']}")
@@ -0,0 +1,39 @@
1
+ from typing import Any, Dict, List
2
+
3
+ from openpyxl import load_workbook
4
+
5
+ from .base import BaseEngine
6
+
7
+
8
+ class OpenpyxlEngine(BaseEngine):
9
+ def __init__(self, file_path: str):
10
+ """
11
+ Initialize OpenpyxlEngine with the given file path.
12
+ """
13
+ self.file_path = file_path
14
+ self.data = self.load()
15
+
16
+ def load(self) -> Dict[str, Any]:
17
+ """
18
+ Load all sheets using openpyxl.
19
+ """
20
+ wb = load_workbook(self.file_path, data_only=True)
21
+ return {sheet: wb[sheet] for sheet in wb.sheetnames}
22
+
23
+ def save(self) -> None:
24
+ """
25
+ Save is not implemented for OpenpyxlEngine.
26
+ """
27
+ raise NotImplementedError(
28
+ "Save operation is not implemented for OpenpyxlEngine."
29
+ )
30
+
31
+ def execute(self, query: str) -> List[Dict[str, Any]]:
32
+ """
33
+ Example execution: return all records from the first sheet.
34
+ """
35
+ sheet = list(self.data.keys())[0]
36
+ ws = self.data[sheet]
37
+ rows = list(ws.iter_rows(values_only=True))
38
+ headers = rows[0]
39
+ return [dict(zip(headers, row)) for row in rows[1:]]
@@ -0,0 +1,49 @@
1
+ from typing import Any, Dict
2
+
3
+ import pandas as pd
4
+ from pandas import DataFrame
5
+
6
+ from .base import BaseEngine
7
+
8
+
9
+ class PandasEngine(BaseEngine):
10
+ """
11
+ PandasEngine uses pandas with openpyxl backend to load and query Excel files.
12
+ """
13
+
14
+ def __init__(self, file_path: str):
15
+ """
16
+ Initialize PandasEngine with the given file path.
17
+ """
18
+ self.file_path = file_path
19
+ self.data = self.load()
20
+
21
+ def load(self) -> Dict[str, DataFrame]:
22
+ """
23
+ Load all sheets as DataFrames using pandas with openpyxl engine.
24
+ """
25
+ try:
26
+ data = pd.read_excel(self.file_path, sheet_name=None, engine="openpyxl")
27
+ return data
28
+ except Exception as e:
29
+ raise IOError(f"Failed to load Excel file: {self.file_path}") from e
30
+
31
+ def save(self, file_path: str) -> None:
32
+ """
33
+ Save the current data back to an Excel file.
34
+ """
35
+ try:
36
+ with pd.ExcelWriter(file_path, engine="openpyxl") as writer:
37
+ for sheet_name, df in self.data.items():
38
+ df.to_excel(writer, sheet_name=sheet_name, index=False)
39
+ except Exception as e:
40
+ raise IOError(f"Failed to save Excel file: {file_path}") from e
41
+
42
+ def execute(self, query: str) -> list[dict[str, Any]]:
43
+ """
44
+ Execute a query and return the result.
45
+
46
+ Currently, only SELECT * FROM [Sheet$] is supported as an example.
47
+ """
48
+ sheet = list(self.data.keys())[0]
49
+ return self.data[sheet].to_dict(orient="records")
@@ -0,0 +1,49 @@
1
+ import re
2
+ from typing import Any, Dict, Optional, Tuple
3
+
4
+
5
+ def parse_sql(query: str, params: Optional[Tuple[Any, ...]] = None) -> Dict[str, Any]:
6
+ query = query.strip()
7
+
8
+ # Parameter binding
9
+ if params:
10
+ placeholders = re.findall(r"\?", query)
11
+ if len(placeholders) != len(params):
12
+ raise ValueError(
13
+ f"Expected {len(placeholders)} parameters, got {len(params)}"
14
+ )
15
+
16
+ for param in params:
17
+ if isinstance(param, str):
18
+ value = f"'{param}'"
19
+ else:
20
+ value = str(param)
21
+ query = query.replace("?", value, 1)
22
+
23
+ result: Dict[str, Any] = {}
24
+ lower_query = query.lower()
25
+
26
+ if lower_query.startswith("select"):
27
+ result["action"] = "SELECT"
28
+ table_match = re.search(r"from\s+\[?(\w+)\$?\]?", query, re.IGNORECASE)
29
+ result["table"] = table_match.group(1).lower() if table_match else None
30
+
31
+ where_match = re.search(r"where\s+(.+)", query, re.IGNORECASE)
32
+ if where_match:
33
+ condition = where_match.group(1).strip()
34
+ # SQL → pandas 변환
35
+ condition = re.sub(r"(?<![=!<>])=(?!=)", "==", condition)
36
+ result["where"] = condition
37
+ else:
38
+ result["where"] = None
39
+
40
+ elif lower_query.startswith("insert"):
41
+ result["action"] = "INSERT"
42
+ table_match = re.search(r"into\s+\[?(\w+)\$?\]?", query, re.IGNORECASE)
43
+ result["table"] = table_match.group(1).lower() if table_match else None
44
+
45
+ else:
46
+ raise NotImplementedError(f"Unsupported SQL: {query}")
47
+
48
+ result["query"] = query
49
+ return result
@@ -0,0 +1,52 @@
1
+ class Error(Exception):
2
+ """Base class for all database-related exceptions."""
3
+
4
+ pass
5
+
6
+
7
+ class DatabaseError(Error):
8
+ """Exception for errors related to the database."""
9
+
10
+ pass
11
+
12
+
13
+ class InterfaceError(Error):
14
+ """Exception for errors related to the database interface."""
15
+
16
+ pass
17
+
18
+
19
+ class DataError(DatabaseError):
20
+ """Exception for errors due to problems with the processed data."""
21
+
22
+ pass
23
+
24
+
25
+ class OperationalError(DatabaseError):
26
+ """Exception for errors related to the database's operation."""
27
+
28
+ pass
29
+
30
+
31
+ class IntegrityError(DatabaseError):
32
+ """Exception for errors related to data integrity."""
33
+
34
+ pass
35
+
36
+
37
+ class InternalError(DatabaseError):
38
+ """Exception for internal database errors."""
39
+
40
+ pass
41
+
42
+
43
+ class ProgrammingError(DatabaseError):
44
+ """Exception for programming errors (SQL syntax, etc.)."""
45
+
46
+ pass
47
+
48
+
49
+ class NotSupportedError(DatabaseError):
50
+ """Exception for unsupported features."""
51
+
52
+ pass
@@ -0,0 +1,22 @@
1
+ Metadata-Version: 2.4
2
+ Name: excel-dbapi
3
+ Version: 2.0.0
4
+ Summary: PEP 249 compliant DB-API driver for Excel files
5
+ Author-email: Yeongseon Choe <yeongseon.choe@gmail.com>
6
+ Requires-Python: >=3.9
7
+ License-File: LICENSE
8
+ Requires-Dist: pandas<3.0.0,>=2.0.0
9
+ Requires-Dist: openpyxl<4.0.0,>=3.1.0
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest<9.0.0,>=8.3.5; extra == "dev"
12
+ Requires-Dist: pytest-cov<5.0.0,>=4.1; extra == "dev"
13
+ Requires-Dist: black<26.0.0,>=25.1.0; extra == "dev"
14
+ Requires-Dist: isort<7.0.0,>=6.0.1; extra == "dev"
15
+ Requires-Dist: ruff<0.12.0,>=0.11.2; extra == "dev"
16
+ Requires-Dist: mypy<2.0.0,>=1.15.0; extra == "dev"
17
+ Requires-Dist: build; extra == "dev"
18
+ Requires-Dist: bandit<2.0.0,>=1.7.0; extra == "dev"
19
+ Requires-Dist: vulture<3.0,>=2.0; extra == "dev"
20
+ Requires-Dist: pre-commit; extra == "dev"
21
+ Requires-Dist: types-requests<3.0.0,>=2.28.0; extra == "dev"
22
+ Dynamic: license-file
@@ -0,0 +1,25 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/excel_dbapi/__init__.py
5
+ src/excel_dbapi/connection.py
6
+ src/excel_dbapi/cursor.py
7
+ src/excel_dbapi/exceptions.py
8
+ src/excel_dbapi.egg-info/PKG-INFO
9
+ src/excel_dbapi.egg-info/SOURCES.txt
10
+ src/excel_dbapi.egg-info/dependency_links.txt
11
+ src/excel_dbapi.egg-info/requires.txt
12
+ src/excel_dbapi.egg-info/top_level.txt
13
+ src/excel_dbapi/engine/__init__.py
14
+ src/excel_dbapi/engine/base.py
15
+ src/excel_dbapi/engine/executor.py
16
+ src/excel_dbapi/engine/openpyxl_engine.py
17
+ src/excel_dbapi/engine/pandas_engine.py
18
+ src/excel_dbapi/engine/parser.py
19
+ tests/test_connection.py
20
+ tests/test_cursor.py
21
+ tests/test_engine.py
22
+ tests/test_exceptions.py
23
+ tests/test_executor.py
24
+ tests/test_integration.py
25
+ tests/test_parser.py
@@ -0,0 +1,15 @@
1
+ pandas<3.0.0,>=2.0.0
2
+ openpyxl<4.0.0,>=3.1.0
3
+
4
+ [dev]
5
+ pytest<9.0.0,>=8.3.5
6
+ pytest-cov<5.0.0,>=4.1
7
+ black<26.0.0,>=25.1.0
8
+ isort<7.0.0,>=6.0.1
9
+ ruff<0.12.0,>=0.11.2
10
+ mypy<2.0.0,>=1.15.0
11
+ build
12
+ bandit<2.0.0,>=1.7.0
13
+ vulture<3.0,>=2.0
14
+ pre-commit
15
+ types-requests<3.0.0,>=2.28.0
@@ -0,0 +1 @@
1
+ excel_dbapi