sqliter-py 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl

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.

Potentially problematic release.


This version of sqliter-py might be problematic. Click here for more details.

sqliter/exceptions.py ADDED
@@ -0,0 +1,114 @@
1
+ """Define custom exceptions for the sqliter package."""
2
+
3
+ import os
4
+ import sys
5
+ import traceback
6
+
7
+
8
+ class SqliterError(Exception):
9
+ """Base class for all exceptions raised by the sqliter package."""
10
+
11
+ message_template: str = "An error occurred in the SQLiter package."
12
+
13
+ def __init__(self, *args: object) -> None:
14
+ """Format the message using the provided arguments.
15
+
16
+ We also capture (and display) the current exception context and chain
17
+ any previous exceptions.
18
+
19
+ :param args: Arguments to format into the message template
20
+ """
21
+ if args:
22
+ message = self.message_template.format(*args)
23
+ else:
24
+ message = (
25
+ self.message_template.replace("'{}'", "")
26
+ .replace(":", "")
27
+ .strip()
28
+ )
29
+
30
+ # Capture the current exception context
31
+ self.original_exception = sys.exc_info()[1]
32
+
33
+ # If there's an active exception, append its information to our message
34
+ if self.original_exception:
35
+ original_type = type(self.original_exception).__name__
36
+ original_module = type(self.original_exception).__module__
37
+
38
+ # Get the traceback of the original exception
39
+ tb = traceback.extract_tb(self.original_exception.__traceback__)
40
+ if tb:
41
+ last_frame = tb[-1]
42
+ file_path = os.path.relpath(last_frame.filename)
43
+ line_number = last_frame.lineno
44
+ location = f"{file_path}:{line_number}"
45
+ else:
46
+ location = "unknown location"
47
+
48
+ message += (
49
+ f"\n --> {original_module}.{original_type} "
50
+ f"from {location}: {self.original_exception}"
51
+ )
52
+
53
+ # Call the parent constructor with our formatted message
54
+ super().__init__(message)
55
+
56
+ # Explicitly chain exceptions if there's an active one
57
+ if self.original_exception:
58
+ self.__cause__ = self.original_exception
59
+
60
+
61
+ class DatabaseConnectionError(SqliterError):
62
+ """Raised when the SQLite database connection fails."""
63
+
64
+ message_template = "Failed to connect to the database: '{}'"
65
+
66
+
67
+ class InvalidOffsetError(SqliterError):
68
+ """Raised when an invalid offset value (0 or negative) is used."""
69
+
70
+ message_template = (
71
+ "Invalid offset value: '{}'. Offset must be a positive integer."
72
+ )
73
+
74
+
75
+ class TableCreationError(SqliterError):
76
+ """Raised when a table cannot be created in the database."""
77
+
78
+ message_template = "Failed to create the table: '{}'"
79
+
80
+
81
+ class RecordInsertionError(SqliterError):
82
+ """Raised when an error occurs during record insertion."""
83
+
84
+ message_template = "Failed to insert record into table: '{}'"
85
+
86
+
87
+ class RecordUpdateError(SqliterError):
88
+ """Raised when an error occurs during record update."""
89
+
90
+ message_template = "Failed to update record in table: '{}'"
91
+
92
+
93
+ class RecordNotFoundError(SqliterError):
94
+ """Raised when a record with the specified primary key is not found."""
95
+
96
+ message_template = "Failed to find a record for key '{}' "
97
+
98
+
99
+ class RecordFetchError(SqliterError):
100
+ """Raised when an error occurs during record fetching."""
101
+
102
+ message_template = "Failed to fetch record from table: '{}'"
103
+
104
+
105
+ class RecordDeletionError(SqliterError):
106
+ """Raised when an error occurs during record deletion."""
107
+
108
+ message_template = "Failed to delete record from table: '{}'"
109
+
110
+
111
+ class InvalidFilterError(SqliterError):
112
+ """Raised when an invalid filter field is used in a query."""
113
+
114
+ message_template = "Failed to apply filter: invalid field '{}'"
sqliter/query/query.py CHANGED
@@ -2,11 +2,18 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import sqlite3
5
6
  from typing import TYPE_CHECKING, Any, Optional
6
7
 
7
8
  from typing_extensions import Self
8
9
 
9
- if TYPE_CHECKING:
10
+ from sqliter.exceptions import (
11
+ InvalidFilterError,
12
+ InvalidOffsetError,
13
+ RecordFetchError,
14
+ )
15
+
16
+ if TYPE_CHECKING: # pragma: no cover
10
17
  from sqliter import SqliterDB
11
18
  from sqliter.model import BaseDBModel
12
19
 
@@ -20,52 +27,87 @@ class QueryBuilder:
20
27
  self.model_class = model_class
21
28
  self.table_name = model_class.get_table_name() # Use model_class method
22
29
  self.filters: list[tuple[str, Any]] = []
30
+ self._limit: Optional[int] = None
31
+ self._offset: Optional[int] = None
32
+ self._order_by: Optional[str] = None
23
33
 
24
34
  def filter(self, **conditions: str | float | None) -> Self:
25
35
  """Add filter conditions to the query."""
36
+ valid_fields = self.model_class.model_fields
37
+
26
38
  for field, value in conditions.items():
39
+ if field not in valid_fields:
40
+ raise InvalidFilterError(field)
27
41
  self.filters.append((field, value))
42
+
43
+ return self
44
+
45
+ def limit(self, limit_value: int) -> Self:
46
+ """Limit the number of results returned by the query."""
47
+ self._limit = limit_value
48
+ return self
49
+
50
+ def offset(self, offset_value: int) -> Self:
51
+ """Set an offset value for the query."""
52
+ if offset_value <= 0:
53
+ raise InvalidOffsetError(offset_value)
54
+ self._offset = offset_value
55
+
56
+ if self._limit is None:
57
+ self._limit = -1
58
+ return self
59
+
60
+ def order(self, order_by_field: str) -> Self:
61
+ """Order the results by a specific field and optionally direction."""
62
+ self._order_by = order_by_field
28
63
  return self
29
64
 
30
65
  def _execute_query(
31
66
  self,
32
- limit: Optional[int] = None,
33
- offset: Optional[int] = None,
34
- order_by: Optional[str] = None,
35
67
  *,
36
68
  fetch_one: bool = False,
37
69
  ) -> list[tuple[Any, ...]] | Optional[tuple[Any, ...]]:
38
70
  """Helper function to execute the query with filters."""
39
71
  fields = ", ".join(self.model_class.model_fields)
72
+
73
+ # Build the WHERE clause with special handling for None (NULL in SQL)
40
74
  where_clause = " AND ".join(
41
- [f"{field} = ?" for field, _ in self.filters]
75
+ [
76
+ f"{field} IS NULL" if value is None else f"{field} = ?"
77
+ for field, value in self.filters
78
+ ]
42
79
  )
80
+
43
81
  sql = f"SELECT {fields} FROM {self.table_name}" # noqa: S608
44
82
 
45
83
  if self.filters:
46
84
  sql += f" WHERE {where_clause}"
47
85
 
48
- if order_by:
49
- sql += f" ORDER BY {order_by}"
86
+ if self._order_by:
87
+ sql += f" ORDER BY {self._order_by}"
50
88
 
51
- if limit is not None:
52
- sql += f" LIMIT {limit}"
89
+ if self._limit is not None:
90
+ sql += f" LIMIT {self._limit}"
53
91
 
54
- if offset is not None:
55
- sql += f" OFFSET {offset}"
92
+ if self._offset is not None:
93
+ sql += f" OFFSET {self._offset}"
56
94
 
57
- values = [value for _, value in self.filters]
95
+ # Only include non-None values in the values list
96
+ values = [value for _, value in self.filters if value is not None]
58
97
 
59
- with self.db.connect() as conn:
60
- cursor = conn.cursor()
61
- cursor.execute(sql, values)
62
- return cursor.fetchall() if not fetch_one else cursor.fetchone()
98
+ try:
99
+ with self.db.connect() as conn:
100
+ cursor = conn.cursor()
101
+ cursor.execute(sql, values)
102
+ return cursor.fetchall() if not fetch_one else cursor.fetchone()
103
+ except sqlite3.Error as exc:
104
+ raise RecordFetchError(self.table_name) from exc
63
105
 
64
106
  def fetch_all(self) -> list[BaseDBModel]:
65
107
  """Fetch all results matching the filters."""
66
108
  results = self._execute_query()
67
109
 
68
- if results is None:
110
+ if not results:
69
111
  return []
70
112
 
71
113
  return [
@@ -92,7 +134,8 @@ class QueryBuilder:
92
134
 
93
135
  def fetch_first(self) -> BaseDBModel | None:
94
136
  """Fetch the first result of the query."""
95
- result = self._execute_query(limit=1)
137
+ self._limit = 1
138
+ result = self._execute_query()
96
139
  if not result:
97
140
  return None
98
141
  return self.model_class(
@@ -103,9 +146,10 @@ class QueryBuilder:
103
146
  )
104
147
 
105
148
  def fetch_last(self) -> BaseDBModel | None:
106
- """Fetch the last result of the query (based on the primary key)."""
107
- primary_key = self.model_class.get_primary_key()
108
- result = self._execute_query(limit=1, order_by=f"{primary_key} DESC")
149
+ """Fetch the last result of the query (based on the insertion order)."""
150
+ self._limit = 1
151
+ self._order_by = "rowid DESC"
152
+ result = self._execute_query()
109
153
  if not result:
110
154
  return None
111
155
  return self.model_class(
sqliter/sqliter.py CHANGED
@@ -7,9 +7,18 @@ from typing import TYPE_CHECKING, Optional
7
7
 
8
8
  from typing_extensions import Self
9
9
 
10
+ from sqliter.exceptions import (
11
+ DatabaseConnectionError,
12
+ RecordDeletionError,
13
+ RecordFetchError,
14
+ RecordInsertionError,
15
+ RecordNotFoundError,
16
+ RecordUpdateError,
17
+ TableCreationError,
18
+ )
10
19
  from sqliter.query.query import QueryBuilder
11
20
 
12
- if TYPE_CHECKING:
21
+ if TYPE_CHECKING: # pragma: no cover
13
22
  from types import TracebackType
14
23
 
15
24
  from sqliter.model.model import BaseDBModel
@@ -27,7 +36,10 @@ class SqliterDB:
27
36
  def connect(self) -> sqlite3.Connection:
28
37
  """Create or return a connection to the SQLite database."""
29
38
  if not self.conn:
30
- self.conn = sqlite3.connect(self.db_filename)
39
+ try:
40
+ self.conn = sqlite3.connect(self.db_filename)
41
+ except sqlite3.Error as exc:
42
+ raise DatabaseConnectionError(self.db_filename) from exc
31
43
  return self.conn
32
44
 
33
45
  def create_table(self, model_class: type[BaseDBModel]) -> None:
@@ -43,7 +55,7 @@ class SqliterDB:
43
55
  if create_id:
44
56
  create_table_sql = f"""
45
57
  CREATE TABLE IF NOT EXISTS {table_name} (
46
- id INTEGER PRIMARY KEY AUTOINCREMENT,
58
+ {primary_key} INTEGER PRIMARY KEY AUTOINCREMENT,
47
59
  {fields}
48
60
  )
49
61
  """
@@ -55,10 +67,13 @@ class SqliterDB:
55
67
  )
56
68
  """
57
69
 
58
- with self.connect() as conn:
59
- cursor = conn.cursor()
60
- cursor.execute(create_table_sql)
61
- conn.commit()
70
+ try:
71
+ with self.connect() as conn:
72
+ cursor = conn.cursor()
73
+ cursor.execute(create_table_sql)
74
+ conn.commit()
75
+ except sqlite3.Error as exc:
76
+ raise TableCreationError(table_name) from exc
62
77
 
63
78
  def _maybe_commit(self, conn: sqlite3.Connection) -> None:
64
79
  """Commit changes if auto_commit is True."""
@@ -77,14 +92,17 @@ class SqliterDB:
77
92
  )
78
93
 
79
94
  insert_sql = f"""
80
- INSERT OR REPLACE INTO {table_name} ({fields})
95
+ INSERT INTO {table_name} ({fields})
81
96
  VALUES ({placeholders})
82
97
  """ # noqa: S608
83
98
 
84
- with self.connect() as conn:
85
- cursor = conn.cursor()
86
- cursor.execute(insert_sql, values)
87
- self._maybe_commit(conn)
99
+ try:
100
+ with self.connect() as conn:
101
+ cursor = conn.cursor()
102
+ cursor.execute(insert_sql, values)
103
+ self._maybe_commit(conn)
104
+ except sqlite3.Error as exc:
105
+ raise RecordInsertionError(table_name) from exc
88
106
 
89
107
  def get(
90
108
  self, model_class: type[BaseDBModel], primary_key_value: str
@@ -99,18 +117,22 @@ class SqliterDB:
99
117
  SELECT {fields} FROM {table_name} WHERE {primary_key} = ?
100
118
  """ # noqa: S608
101
119
 
102
- with self.connect() as conn:
103
- cursor = conn.cursor()
104
- cursor.execute(select_sql, (primary_key_value,))
105
- result = cursor.fetchone()
106
-
107
- if result:
108
- result_dict = {
109
- field: result[idx]
110
- for idx, field in enumerate(model_class.model_fields)
111
- }
112
- return model_class(**result_dict)
113
- return None
120
+ try:
121
+ with self.connect() as conn:
122
+ cursor = conn.cursor()
123
+ cursor.execute(select_sql, (primary_key_value,))
124
+ result = cursor.fetchone()
125
+
126
+ if result:
127
+ result_dict = {
128
+ field: result[idx]
129
+ for idx, field in enumerate(model_class.model_fields)
130
+ }
131
+ return model_class(**result_dict)
132
+ except sqlite3.Error as exc:
133
+ raise RecordFetchError(table_name) from exc
134
+ else:
135
+ return None
114
136
 
115
137
  def update(self, model_instance: BaseDBModel) -> None:
116
138
  """Update an existing record using the Pydantic model."""
@@ -131,13 +153,24 @@ class SqliterDB:
131
153
  primary_key_value = getattr(model_instance, primary_key)
132
154
 
133
155
  update_sql = f"""
134
- UPDATE {table_name} SET {fields} WHERE {primary_key} = ?
156
+ UPDATE {table_name}
157
+ SET {fields}
158
+ WHERE {primary_key} = ?
135
159
  """ # noqa: S608
136
160
 
137
- with self.connect() as conn:
138
- cursor = conn.cursor()
139
- cursor.execute(update_sql, (*values, primary_key_value))
140
- self._maybe_commit(conn)
161
+ try:
162
+ with self.connect() as conn:
163
+ cursor = conn.cursor()
164
+ cursor.execute(update_sql, (*values, primary_key_value))
165
+
166
+ # Check if any rows were updated
167
+ if cursor.rowcount == 0:
168
+ raise RecordNotFoundError(primary_key_value)
169
+
170
+ self._maybe_commit(conn)
171
+
172
+ except sqlite3.Error as exc:
173
+ raise RecordUpdateError(table_name) from exc
141
174
 
142
175
  def delete(
143
176
  self, model_class: type[BaseDBModel], primary_key_value: str
@@ -150,10 +183,16 @@ class SqliterDB:
150
183
  DELETE FROM {table_name} WHERE {primary_key} = ?
151
184
  """ # noqa: S608
152
185
 
153
- with self.connect() as conn:
154
- cursor = conn.cursor()
155
- cursor.execute(delete_sql, (primary_key_value,))
156
- self._maybe_commit(conn)
186
+ try:
187
+ with self.connect() as conn:
188
+ cursor = conn.cursor()
189
+ cursor.execute(delete_sql, (primary_key_value,))
190
+
191
+ if cursor.rowcount == 0:
192
+ raise RecordNotFoundError(primary_key_value)
193
+ self._maybe_commit(conn)
194
+ except sqlite3.Error as exc:
195
+ raise RecordDeletionError(table_name) from exc
157
196
 
158
197
  def select(self, model_class: type[BaseDBModel]) -> QueryBuilder:
159
198
  """Start a query for the given model."""
@@ -173,7 +212,12 @@ class SqliterDB:
173
212
  ) -> None:
174
213
  """Exit the runtime context and close the connection."""
175
214
  if self.conn:
176
- if not self.auto_commit:
177
- self.conn.commit()
178
- self.conn.close()
179
- self.conn = None
215
+ try:
216
+ if exc_type:
217
+ # Roll back the transaction if there was an exception
218
+ self.conn.rollback()
219
+ self._maybe_commit(self.conn)
220
+ finally:
221
+ # Close the connection and reset the instance variable
222
+ self.conn.close()
223
+ self.conn = None
@@ -0,0 +1,204 @@
1
+ Metadata-Version: 2.3
2
+ Name: sqliter-py
3
+ Version: 0.1.1
4
+ Summary: Interact with SQLite databases using Python and Pydantic
5
+ Project-URL: Pull Requests, https://github.com/seapagan/sqliter-py/pulls
6
+ Project-URL: Bug Tracker, https://github.com/seapagan/sqliter-py/issues
7
+ Project-URL: Repository, https://github.com/seapagan/sqliter-py
8
+ Author-email: Grant Ramsay <grant@gnramsay.com>
9
+ License-Expression: MIT
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.9
22
+ Requires-Dist: pydantic>=2.9.0
23
+ Description-Content-Type: text/markdown
24
+
25
+ # SQLiter
26
+
27
+ <!-- ![PyPI - Version](https://img.shields.io/pypi/v/sqliter-py) -->
28
+ [![Test Suite](https://github.com/seapagan/sqliter-py/actions/workflows/testing.yml/badge.svg)](https://github.com/seapagan/sqliter-py/actions/workflows/testing.yml)
29
+ [![Linting](https://github.com/seapagan/sqliter-py/actions/workflows/linting.yml/badge.svg)](https://github.com/seapagan/sqliter-py/actions/workflows/linting.yml)
30
+ [![Type Checking](https://github.com/seapagan/sqliter-py/actions/workflows/mypy.yml/badge.svg)](https://github.com/seapagan/sqliter-py/actions/workflows/mypy.yml)
31
+ <!-- ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/sqliter-py) -->
32
+
33
+ SQLiter is a lightweight Object-Relational Mapping (ORM) library for SQLite
34
+ databases in Python. It provides a simplified interface for interacting with
35
+ SQLite databases using Pydantic models.
36
+
37
+ It does not aim to be a full-fledged ORM like SQLAlchemy, but rather a simple
38
+ and easy-to-use library for basic database operations, especially for small
39
+ projects. It is NOT asynchronous and does not support complex queries (at this
40
+ time).
41
+
42
+ The ideal use case is more for Python CLI tools that need to store data in a
43
+ database-like format without needing to learn SQL or use a full ORM.
44
+
45
+ > [!NOTE]
46
+ > This project is still in the early stages of development and is lacking some
47
+ > planned functionality. Please use with caution.
48
+ >
49
+ > See the [TODO](TODO.md) for planned features and improvements.
50
+
51
+ ## Features
52
+
53
+ - Table creation based on Pydantic models
54
+ - CRUD operations (Create, Read, Update, Delete)
55
+ - Basic query building with filtering, ordering, and pagination
56
+ - Transaction support
57
+ - Custom exceptions for better error handling
58
+
59
+ ## Installation
60
+
61
+ You can install SQLiter using pip:
62
+
63
+ ```bash
64
+ pip install sqliter-py
65
+ ```
66
+
67
+ ## Quick Start
68
+
69
+ Here's a quick example of how to use SQLiter:
70
+
71
+ ```python
72
+ from sqliter import SqliterDB
73
+ from sqliter.model import BaseDBModel
74
+
75
+ # Define your model
76
+ class User(BaseDBModel):
77
+ name: str
78
+ age: int
79
+
80
+ class Meta:
81
+ table_name = "users"
82
+
83
+ # Create a database connection
84
+ db = SqliterDB("example.db", auto_commit=True)
85
+
86
+ # Create the table
87
+ db.create_table(User)
88
+
89
+ # Insert a record
90
+ user = User(name="John Doe", age=30)
91
+ db.insert(user)
92
+
93
+ # Query records
94
+ results = db.select(User).filter(name="John Doe").fetch_all()
95
+ for user in results:
96
+ print(f"User: {user.name}, Age: {user.age}")
97
+
98
+ # Update a record
99
+ user.age = 31
100
+ db.update(user)
101
+
102
+ # Delete a record
103
+ db.delete(User, "John Doe")
104
+ ```
105
+
106
+ ## Detailed Usage
107
+
108
+ ### Defining Models
109
+
110
+ Models in SQLiter are based on Pydantic's `BaseModel`. You can define your
111
+ models like this:
112
+
113
+ ```python
114
+ from sqliter.model import BaseDBModel
115
+
116
+ class User(BaseDBModel):
117
+ name: str
118
+ age: int
119
+ email: str
120
+
121
+ class Meta:
122
+ table_name = "users"
123
+ primary_key = "name" # Default is "id"
124
+ create_id = False # Set to True to auto-create an ID field
125
+ ```
126
+
127
+ ### Database Operations
128
+
129
+ #### Creating a Connection
130
+
131
+ ```python
132
+ from sqliter import SqliterDB
133
+
134
+ db = SqliterDB("your_database.db", auto_commit=True)
135
+ ```
136
+
137
+ #### Creating Tables
138
+
139
+ ```python
140
+ db.create_table(User)
141
+ ```
142
+
143
+ #### Inserting Records
144
+
145
+ ```python
146
+ user = User(name="Jane Doe", age=25, email="jane@example.com")
147
+ db.insert(user)
148
+ ```
149
+
150
+ #### Querying Records
151
+
152
+ ```python
153
+ # Fetch all users
154
+ all_users = db.select(User).fetch_all()
155
+
156
+ # Filter users
157
+ young_users = db.select(User).filter(age=25).fetch_all()
158
+
159
+ # Order users
160
+ ordered_users = db.select(User).order("age DESC").fetch_all()
161
+
162
+ # Limit and offset
163
+ paginated_users = db.select(User).limit(10).offset(20).fetch_all()
164
+ ```
165
+
166
+ Note: The filtering in SQLiter is basic and supports exact matches. Complex
167
+ queries like `age__lt` are not supported in the current implementation.
168
+
169
+ #### Updating Records
170
+
171
+ ```python
172
+ user.age = 26
173
+ db.update(user)
174
+ ```
175
+
176
+ #### Deleting Records
177
+
178
+ ```python
179
+ db.delete(User, "Jane Doe")
180
+ ```
181
+
182
+ ### Transactions
183
+
184
+ SQLiter supports transactions using Python's context manager:
185
+
186
+ ```python
187
+ with db:
188
+ db.insert(User(name="Alice", age=30, email="alice@example.com"))
189
+ db.insert(User(name="Bob", age=35, email="bob@example.com"))
190
+ # If an exception occurs, the transaction will be rolled back
191
+ ```
192
+
193
+ ## Contributing
194
+
195
+ Contributions are welcome! Please feel free to submit a Pull Request.
196
+
197
+ ## License
198
+
199
+ This project is licensed under the MIT License.
200
+
201
+ ## Acknowledgements
202
+
203
+ SQLiter was initially developed as an experiment using ChatGPT, with subsequent
204
+ manual refinements and improvements.
@@ -0,0 +1,10 @@
1
+ sqliter/__init__.py,sha256=L8R0uvCbbbACwaI5xtd3khtvpNhlPRgHJAaYZvqjzig,134
2
+ sqliter/exceptions.py,sha256=5NO9DwHMVKrDZP908G63J5-ANBJDCwbBkXL7h_Bit2Q,3610
3
+ sqliter/sqliter.py,sha256=R69oNusPEg0Q_zRPFetS9VUUCT5pjatXnUr3Tv6yzjE,7494
4
+ sqliter/model/__init__.py,sha256=Ovpkbyx2-T6Oee0qFNgUBBc2M0uwK-cdG0pigG3mkd8,179
5
+ sqliter/model/model.py,sha256=t1w38om37gma1gRk01Z_9II0h4g-l734ijN_8M1SYoY,1247
6
+ sqliter/query/__init__.py,sha256=BluNMJpuoo2PsYN-bL7fXlEc02O_8LgOMsvCmyv04ao,125
7
+ sqliter/query/query.py,sha256=BRKCdMBOlbcIU5CUxRyKBF4aFT2AboUPu7UwbJ1s5hs,5793
8
+ sqliter_py-0.1.1.dist-info/METADATA,sha256=ehe8KZXc08ftvn2i6FGiw3Qe8jK6QsqApDoDQm9p0XE,5477
9
+ sqliter_py-0.1.1.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
10
+ sqliter_py-0.1.1.dist-info/RECORD,,
@@ -1,30 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: sqliter-py
3
- Version: 0.1.0
4
- Summary: Interact with SQLite databases using Python and Pydantic
5
- Project-URL: Pull Requests, https://github.com/seapagan/sqliter-py/pulls
6
- Project-URL: Bug Tracker, https://github.com/seapagan/sqliter-py/issues
7
- Project-URL: Repository, https://github.com/seapagan/sqliter-py
8
- Author-email: Grant Ramsay <grant@gnramsay.com>
9
- License-Expression: MIT
10
- Classifier: Development Status :: 4 - Beta
11
- Classifier: Intended Audience :: Developers
12
- Classifier: License :: OSI Approved :: MIT License
13
- Classifier: Operating System :: OS Independent
14
- Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.9
16
- Classifier: Programming Language :: Python :: 3.10
17
- Classifier: Programming Language :: Python :: 3.11
18
- Classifier: Programming Language :: Python :: 3.12
19
- Classifier: Topic :: Software Development
20
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
- Requires-Python: >=3.9
22
- Requires-Dist: pydantic>=2.9.0
23
- Description-Content-Type: text/markdown
24
-
25
- # SQLiter
26
-
27
- An SQLite wrapper in Python using Pydantic and written primarily using ChatGPT,
28
- as an experiment in how viable it is to write working code using a LLM.
29
-
30
- The code was then cleaned up, typed and linted by hand.
@@ -1,9 +0,0 @@
1
- sqliter/__init__.py,sha256=L8R0uvCbbbACwaI5xtd3khtvpNhlPRgHJAaYZvqjzig,134
2
- sqliter/sqliter.py,sha256=FxWtYnjnfM2Tp1lc1b1AfrGia-G0IdSopZ2bSa-lPuo,5944
3
- sqliter/model/__init__.py,sha256=Ovpkbyx2-T6Oee0qFNgUBBc2M0uwK-cdG0pigG3mkd8,179
4
- sqliter/model/model.py,sha256=t1w38om37gma1gRk01Z_9II0h4g-l734ijN_8M1SYoY,1247
5
- sqliter/query/__init__.py,sha256=BluNMJpuoo2PsYN-bL7fXlEc02O_8LgOMsvCmyv04ao,125
6
- sqliter/query/query.py,sha256=8wV5GJwQVFesnW60Qe9XCiOwEZI3Wvk-8WNM0ANsT_M,4442
7
- sqliter_py-0.1.0.dist-info/METADATA,sha256=R_IogiSpgnGC54Mftva2w5EIa-JnGH-R8NuE5_LDhRo,1267
8
- sqliter_py-0.1.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
9
- sqliter_py-0.1.0.dist-info/RECORD,,