sqliter-py 0.2.0__py3-none-any.whl → 0.4.0__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/__init__.py CHANGED
@@ -1,4 +1,8 @@
1
- """The 'sqliter' package provides an minimal ORM for the SQLite library."""
1
+ """SQLiter: A lightweight ORM-like library for SQLite databases in Python.
2
+
3
+ This module provides the main SqliterDB class for interacting with
4
+ SQLite databases using Pydantic models.
5
+ """
2
6
 
3
7
  from .sqliter import SqliterDB
4
8
 
sqliter/constants.py CHANGED
@@ -1,5 +1,13 @@
1
- """Define constants used in the library."""
1
+ """Constant values and mappings used throughout SQLiter.
2
2
 
3
+ This module defines constant dictionaries that map SQLiter-specific
4
+ concepts to their SQLite equivalents. It includes mappings for query
5
+ operators and data types, which are crucial for translating between
6
+ Pydantic models and SQLite database operations.
7
+ """
8
+
9
+ # A dictionary mapping SQLiter filter operators to their corresponding SQL
10
+ # operators.
3
11
  OPERATOR_MAPPING = {
4
12
  "__lt": "<",
5
13
  "__lte": "<=",
@@ -18,3 +26,12 @@ OPERATOR_MAPPING = {
18
26
  "__iendswith": "LIKE",
19
27
  "__icontains": "LIKE",
20
28
  }
29
+
30
+ # A dictionary mapping Python types to their corresponding SQLite column types.
31
+ SQLITE_TYPE_MAPPING = {
32
+ int: "INTEGER",
33
+ float: "REAL",
34
+ str: "TEXT",
35
+ bool: "INTEGER", # SQLite stores booleans as integers (0 or 1)
36
+ bytes: "BLOB",
37
+ }
sqliter/exceptions.py CHANGED
@@ -1,4 +1,11 @@
1
- """Define custom exceptions for the sqliter package."""
1
+ """Custom exception classes for SQLiter error handling.
2
+
3
+ This module defines a hierarchy of exception classes specific to
4
+ SQLiter operations. These exceptions provide detailed error information
5
+ for various scenarios such as connection issues, invalid queries,
6
+ and CRUD operation failures, enabling more precise error handling
7
+ in applications using SQLiter.
8
+ """
2
9
 
3
10
  import os
4
11
  import sys
@@ -6,7 +13,15 @@ import traceback
6
13
 
7
14
 
8
15
  class SqliterError(Exception):
9
- """Base class for all exceptions raised by the sqliter package."""
16
+ """Base exception class for all SQLiter-specific errors.
17
+
18
+ This class serves as the parent for all custom exceptions in SQLiter,
19
+ providing a consistent interface and message formatting.
20
+
21
+ Attributes:
22
+ message_template (str): A template string for the error message.
23
+ original_exception (Exception): The original exception that was caught.
24
+ """
10
25
 
11
26
  message_template: str = "An error occurred in the SQLiter package."
12
27
 
@@ -59,13 +74,13 @@ class SqliterError(Exception):
59
74
 
60
75
 
61
76
  class DatabaseConnectionError(SqliterError):
62
- """Raised when the SQLite database connection fails."""
77
+ """Exception raised when a database connection cannot be established."""
63
78
 
64
79
  message_template = "Failed to connect to the database: '{}'"
65
80
 
66
81
 
67
82
  class InvalidOffsetError(SqliterError):
68
- """Raised when an invalid offset value (0 or negative) is used."""
83
+ """Exception raised when an invalid offset value is provided."""
69
84
 
70
85
  message_template = (
71
86
  "Invalid offset value: '{}'. Offset must be a positive integer."
@@ -73,48 +88,60 @@ class InvalidOffsetError(SqliterError):
73
88
 
74
89
 
75
90
  class InvalidOrderError(SqliterError):
76
- """Raised when an invalid order value is used."""
91
+ """Exception raised when an invalid order specification is provided."""
77
92
 
78
93
  message_template = "Invalid order value - {}"
79
94
 
80
95
 
81
96
  class TableCreationError(SqliterError):
82
- """Raised when a table cannot be created in the database."""
97
+ """Exception raised when a table cannot be created in the database."""
83
98
 
84
99
  message_template = "Failed to create the table: '{}'"
85
100
 
86
101
 
87
102
  class RecordInsertionError(SqliterError):
88
- """Raised when an error occurs during record insertion."""
103
+ """Exception raised when a record cannot be inserted into the database."""
89
104
 
90
105
  message_template = "Failed to insert record into table: '{}'"
91
106
 
92
107
 
93
108
  class RecordUpdateError(SqliterError):
94
- """Raised when an error occurs during record update."""
109
+ """Exception raised when a record cannot be updated in the database."""
95
110
 
96
111
  message_template = "Failed to update record in table: '{}'"
97
112
 
98
113
 
99
114
  class RecordNotFoundError(SqliterError):
100
- """Raised when a record with the specified primary key is not found."""
115
+ """Exception raised when a requested record is not found in the database."""
101
116
 
102
117
  message_template = "Failed to find a record for key '{}' "
103
118
 
104
119
 
105
120
  class RecordFetchError(SqliterError):
106
- """Raised when an error occurs during record fetching."""
121
+ """Exception raised on an error fetching records from the database."""
107
122
 
108
123
  message_template = "Failed to fetch record from table: '{}'"
109
124
 
110
125
 
111
126
  class RecordDeletionError(SqliterError):
112
- """Raised when an error occurs during record deletion."""
127
+ """Exception raised when a record cannot be deleted from the database."""
113
128
 
114
129
  message_template = "Failed to delete record from table: '{}'"
115
130
 
116
131
 
117
132
  class InvalidFilterError(SqliterError):
118
- """Raised when an invalid filter field is used in a query."""
133
+ """Exception raised when an invalid filter is applied to a query."""
119
134
 
120
135
  message_template = "Failed to apply filter: invalid field '{}'"
136
+
137
+
138
+ class TableDeletionError(SqliterError):
139
+ """Raised when a table cannot be deleted from the database."""
140
+
141
+ message_template = "Failed to delete the table: '{}'"
142
+
143
+
144
+ class SqlExecutionError(SqliterError):
145
+ """Raised when an SQL execution fails."""
146
+
147
+ message_template = "Failed to execute SQL: '{}'"
sqliter/helpers.py ADDED
@@ -0,0 +1,35 @@
1
+ """Utility functions for SQLiter internal operations.
2
+
3
+ This module provides helper functions used across the SQLiter library,
4
+ primarily for type inference and mapping between Python and SQLite
5
+ data types. These utilities support the core functionality of model
6
+ to database schema translation.
7
+ """
8
+
9
+ from typing import Union
10
+
11
+ from sqliter.constants import SQLITE_TYPE_MAPPING
12
+
13
+
14
+ def infer_sqlite_type(field_type: Union[type, None]) -> str:
15
+ """Infer the SQLite column type based on the Python type.
16
+
17
+ This function maps Python types to their corresponding SQLite column
18
+ types. It's used when creating database tables to ensure that the
19
+ correct SQLite types are used for each field.
20
+
21
+ Args:
22
+ field_type: The Python type of the field, or None.
23
+
24
+ Returns:
25
+ A string representing the corresponding SQLite column type.
26
+
27
+ Note:
28
+ If the input type is None or not recognized, it defaults to 'TEXT'.
29
+ """
30
+ # If field_type is None, default to TEXT
31
+ if field_type is None:
32
+ return "TEXT"
33
+
34
+ # Map the simplified type to an SQLite type
35
+ return SQLITE_TYPE_MAPPING.get(field_type, "TEXT")
sqliter/model/__init__.py CHANGED
@@ -1,6 +1,7 @@
1
- """This module defines the BaseDBModel class.
1
+ """This module provides the base model class for SQLiter database models.
2
2
 
3
- This should be subclassed by the user to interact with the database.
3
+ It exports the BaseDBModel class, which is used to define database
4
+ models in SQLiter applications.
4
5
  """
5
6
 
6
7
  from .model import BaseDBModel
sqliter/model/model.py CHANGED
@@ -1,38 +1,144 @@
1
- """Define the Base model class."""
1
+ """Defines the base model class for SQLiter ORM functionality.
2
+
3
+ This module provides the BaseDBModel class, which extends Pydantic's
4
+ BaseModel to add SQLiter-specific functionality. It includes methods
5
+ for table name inference, primary key management, and partial model
6
+ validation, forming the foundation for defining database-mapped models
7
+ in SQLiter applications.
8
+ """
2
9
 
3
10
  from __future__ import annotations
4
11
 
5
- from typing import Optional
12
+ import re
13
+ from typing import Any, Optional, TypeVar, Union, get_args, get_origin
14
+
15
+ from pydantic import BaseModel, ConfigDict
6
16
 
7
- from pydantic import BaseModel
17
+ T = TypeVar("T", bound="BaseDBModel")
8
18
 
9
19
 
10
20
  class BaseDBModel(BaseModel):
11
- """Custom base model for database models."""
21
+ """Base model class for SQLiter database models.
22
+
23
+ This class extends Pydantic's BaseModel to provide additional functionality
24
+ for database operations. It includes configuration options and methods
25
+ specific to SQLiter's ORM-like functionality.
26
+
27
+ This should not be used directly, but should be inherited by subclasses
28
+ representing database models.
29
+ """
30
+
31
+ model_config = ConfigDict(
32
+ extra="ignore",
33
+ populate_by_name=True,
34
+ validate_assignment=False,
35
+ from_attributes=True,
36
+ )
12
37
 
13
38
  class Meta:
14
- """Configure the base model with default options."""
39
+ """Metadata class for configuring database-specific attributes.
40
+
41
+ Attributes:
42
+ create_pk (bool): Whether to create a primary key field.
43
+ primary_key (str): The name of the primary key field.
44
+ table_name (Optional[str]): The name of the database table.
45
+ """
15
46
 
16
- create_id: bool = True # Whether to create an auto-increment ID
17
- primary_key: str = "id" # Default primary key field
47
+ create_pk: bool = (
48
+ True # Whether to create an auto-increment primary key
49
+ )
50
+ primary_key: str = "id" # Default primary key name
18
51
  table_name: Optional[str] = (
19
52
  None # Table name, defaults to class name if not set
20
53
  )
21
54
 
55
+ @classmethod
56
+ def model_validate_partial(cls: type[T], obj: dict[str, Any]) -> T:
57
+ """Validate and create a model instance from partial data.
58
+
59
+ This method allows for the creation of a model instance even when
60
+ not all fields are present in the input data.
61
+
62
+ Args:
63
+ obj: A dictionary of field names and values.
64
+
65
+ Returns:
66
+ An instance of the model class with the provided data.
67
+ """
68
+ converted_obj: dict[str, Any] = {}
69
+ for field_name, value in obj.items():
70
+ field = cls.model_fields[field_name]
71
+ field_type: Optional[type] = field.annotation
72
+ if (
73
+ field_type is None or value is None
74
+ ): # Direct check for None values here
75
+ converted_obj[field_name] = None
76
+ else:
77
+ origin = get_origin(field_type)
78
+ if origin is Union:
79
+ args = get_args(field_type)
80
+ for arg in args:
81
+ try:
82
+ # Try converting the value to the type
83
+ converted_obj[field_name] = arg(value)
84
+ break
85
+ except (ValueError, TypeError):
86
+ pass
87
+ else:
88
+ converted_obj[field_name] = value
89
+ else:
90
+ converted_obj[field_name] = field_type(value)
91
+
92
+ return cls.model_construct(**converted_obj)
93
+
22
94
  @classmethod
23
95
  def get_table_name(cls) -> str:
24
- """Get the table name from the Meta, or default to the classname."""
96
+ """Get the database table name for the model.
97
+
98
+ This method determines the table name based on the Meta configuration
99
+ or derives it from the class name if not explicitly set.
100
+
101
+ Returns:
102
+ The name of the database table for this model.
103
+ """
25
104
  table_name: str | None = getattr(cls.Meta, "table_name", None)
26
105
  if table_name is not None:
27
106
  return table_name
28
- return cls.__name__.lower() # Default to class name in lowercase
107
+
108
+ # Get class name and remove 'Model' suffix if present
109
+ class_name = cls.__name__.removesuffix("Model")
110
+
111
+ # Convert to snake_case
112
+ snake_case_name = re.sub(r"(?<!^)(?=[A-Z])", "_", class_name).lower()
113
+
114
+ # Pluralize the table name
115
+ try:
116
+ import inflect
117
+
118
+ p = inflect.engine()
119
+ return p.plural(snake_case_name)
120
+ except ImportError:
121
+ # Fallback to simple pluralization by adding 's'
122
+ return (
123
+ snake_case_name
124
+ if snake_case_name.endswith("s")
125
+ else snake_case_name + "s"
126
+ )
29
127
 
30
128
  @classmethod
31
129
  def get_primary_key(cls) -> str:
32
- """Get the primary key from the Meta class or default to 'id'."""
130
+ """Get the primary key field name for the model.
131
+
132
+ Returns:
133
+ The name of the primary key field.
134
+ """
33
135
  return getattr(cls.Meta, "primary_key", "id")
34
136
 
35
137
  @classmethod
36
- def should_create_id(cls) -> bool:
37
- """Check whether the model should create an auto-increment ID."""
38
- return getattr(cls.Meta, "create_id", True)
138
+ def should_create_pk(cls) -> bool:
139
+ """Determine if a primary key should be automatically created.
140
+
141
+ Returns:
142
+ True if a primary key should be created, False otherwise.
143
+ """
144
+ return getattr(cls.Meta, "create_pk", True)
sqliter/query/__init__.py CHANGED
@@ -1,4 +1,8 @@
1
- """Define the 'QueryBuilder' class for building SQL queries."""
1
+ """This module provides the query building functionality for SQLiter.
2
+
3
+ It exports the QueryBuilder class, which is used to construct and
4
+ execute database queries in SQLiter.
5
+ """
2
6
 
3
7
  from .query import QueryBuilder
4
8