iceaxe 0.7.0.dev1__cp312-cp312-macosx_11_0_arm64.whl → 0.7.0.dev2__cp312-cp312-macosx_11_0_arm64.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 iceaxe might be problematic. Click here for more details.

@@ -1,3 +1,4 @@
1
+ import warnings
1
2
  from datetime import date, datetime, time, timedelta
2
3
  from enum import Enum, IntEnum, StrEnum
3
4
  from typing import Generic, Sequence, TypeVar
@@ -20,6 +21,7 @@ from iceaxe.schemas.actions import (
20
21
  DryRunComment,
21
22
  )
22
23
  from iceaxe.schemas.db_memory_serializer import (
24
+ CompositePrimaryKeyConstraintError,
23
25
  DatabaseHandler,
24
26
  DatabaseMemorySerializer,
25
27
  )
@@ -1462,3 +1464,62 @@ def test_foreign_key_actions():
1462
1464
  assert fk_constraint.foreign_key_constraint.target_columns == frozenset({"id"})
1463
1465
  assert fk_constraint.foreign_key_constraint.on_delete == "CASCADE"
1464
1466
  assert fk_constraint.foreign_key_constraint.on_update == "CASCADE"
1467
+
1468
+
1469
+ def test_multiple_primary_keys_foreign_key_error():
1470
+ """
1471
+ Test that when a model has multiple primary keys and foreign key constraints,
1472
+ we get a helpful error message explaining the issue.
1473
+ """
1474
+
1475
+ class User(TableBase):
1476
+ id: int = Field(primary_key=True)
1477
+ tenant_id: int = Field(primary_key=True) # Composite primary key
1478
+ name: str
1479
+
1480
+ class Topic(TableBase):
1481
+ id: str = Field(primary_key=True)
1482
+ tenant_id: int = Field(primary_key=True) # Composite primary key
1483
+ title: str
1484
+
1485
+ class Rec(TableBase):
1486
+ id: int = Field(primary_key=True, default=None)
1487
+ creator_id: int = Field(
1488
+ foreign_key="user.id"
1489
+ ) # This will fail because user is leveraging our synthetic primary key
1490
+ topic_id: str = Field(
1491
+ foreign_key="topic.id"
1492
+ ) # This will fail because topic is leveraging our synthetic primary key
1493
+
1494
+ migrator = DatabaseMemorySerializer()
1495
+
1496
+ with pytest.raises(CompositePrimaryKeyConstraintError) as exc_info:
1497
+ db_objects = list(migrator.delegate([User, Topic, Rec]))
1498
+ migrator.order_db_objects(db_objects)
1499
+
1500
+ # Check that the exception has the expected attributes
1501
+ assert exc_info.value.missing_constraints == [("user", "id")]
1502
+
1503
+
1504
+ def test_multiple_primary_keys_warning():
1505
+ """
1506
+ Test that when a model has multiple primary keys, we get a warning.
1507
+ """
1508
+
1509
+ class ExampleModel(TableBase):
1510
+ value_a: int = Field(primary_key=True)
1511
+ value_b: int = Field(primary_key=True)
1512
+
1513
+ migrator = DatabaseMemorySerializer()
1514
+
1515
+ with warnings.catch_warnings(record=True) as w:
1516
+ warnings.simplefilter("always")
1517
+ list(migrator.delegate([ExampleModel]))
1518
+
1519
+ # Check that a warning was issued
1520
+ assert len(w) == 1
1521
+ assert issubclass(w[0].category, UserWarning)
1522
+ warning_message = str(w[0].message)
1523
+ assert "multiple fields marked as primary_key=True" in warning_message
1524
+ assert "composite primary key constraint" in warning_message
1525
+ assert "Consider using only one primary key field" in warning_message
@@ -1,4 +1,168 @@
1
- from iceaxe.schemas.db_stubs import DBType
1
+ import pytest
2
+
3
+ from iceaxe.schemas.db_stubs import ConstraintPointerInfo, DBObjectPointer, DBType
4
+
5
+
6
+ class MockDBObjectPointer(DBObjectPointer):
7
+ """Mock implementation of DBObjectPointer for testing parser methods."""
8
+
9
+ representation_str: str
10
+
11
+ def representation(self) -> str:
12
+ return self.representation_str
13
+
14
+
15
+ @pytest.mark.parametrize(
16
+ "representation_str,expected_result",
17
+ [
18
+ # Valid constraint pointer formats
19
+ (
20
+ "users.['id'].PRIMARY KEY",
21
+ ConstraintPointerInfo("users", ["id"], "PRIMARY KEY"),
22
+ ),
23
+ (
24
+ "orders.['user_id', 'product_id'].UNIQUE",
25
+ ConstraintPointerInfo("orders", ["user_id", "product_id"], "UNIQUE"),
26
+ ),
27
+ (
28
+ "products.['name'].INDEX",
29
+ ConstraintPointerInfo("products", ["name"], "INDEX"),
30
+ ),
31
+ (
32
+ "table_name.['col1', 'col2', 'col3'].FOREIGN KEY",
33
+ ConstraintPointerInfo(
34
+ "table_name", ["col1", "col2", "col3"], "FOREIGN KEY"
35
+ ),
36
+ ),
37
+ # Single quotes
38
+ ("users.['email'].UNIQUE", ConstraintPointerInfo("users", ["email"], "UNIQUE")),
39
+ # Double quotes
40
+ ('users.["email"].UNIQUE', ConstraintPointerInfo("users", ["email"], "UNIQUE")),
41
+ # Mixed quotes
42
+ (
43
+ "users.[\"col1\", 'col2'].UNIQUE",
44
+ ConstraintPointerInfo("users", ["col1", "col2"], "UNIQUE"),
45
+ ),
46
+ # Extra whitespace
47
+ (
48
+ "users.[ 'col1' , 'col2' ].UNIQUE",
49
+ ConstraintPointerInfo("users", ["col1", "col2"], "UNIQUE"),
50
+ ),
51
+ # Empty column list
52
+ ("users.[].CHECK", ConstraintPointerInfo("users", [], "CHECK")),
53
+ # Schema-qualified table names (dots in table names are valid when representing schema.table)
54
+ (
55
+ "public.users.['column'].PRIMARY KEY",
56
+ ConstraintPointerInfo("public.users", ["column"], "PRIMARY KEY"),
57
+ ),
58
+ # Complex constraint types
59
+ (
60
+ "users.['id'].PRIMARY KEY AUTOINCREMENT",
61
+ ConstraintPointerInfo("users", ["id"], "PRIMARY KEY AUTOINCREMENT"),
62
+ ),
63
+ # Table names with underscores and numbers (valid PostgreSQL identifiers)
64
+ (
65
+ "user_table_2.['id'].PRIMARY KEY",
66
+ ConstraintPointerInfo("user_table_2", ["id"], "PRIMARY KEY"),
67
+ ),
68
+ # Column names with underscores and numbers
69
+ (
70
+ "users.['user_id_2', 'created_at'].UNIQUE",
71
+ ConstraintPointerInfo("users", ["user_id_2", "created_at"], "UNIQUE"),
72
+ ),
73
+ # Invalid formats that should return None
74
+ ("users.column.UNIQUE", None), # Missing brackets
75
+ ("users.['column']", None), # Missing constraint type
76
+ ("['column'].UNIQUE", None), # Missing table name
77
+ ("users", None), # Just table name
78
+ ("", None), # Empty string
79
+ ("users.column", None), # Simple table.column format
80
+ ("invalid_format", None), # Random string
81
+ # Malformed bracket syntax
82
+ ("users.[column].UNIQUE", None), # Missing quotes in brackets
83
+ ("users.['column.UNIQUE", None), # Unclosed bracket
84
+ ("users.column'].UNIQUE", None), # Missing opening bracket
85
+ ],
86
+ )
87
+ def test_parse_constraint_pointer(
88
+ representation_str: str, expected_result: ConstraintPointerInfo | None
89
+ ):
90
+ """Test parsing of constraint pointer representations."""
91
+ pointer = MockDBObjectPointer(representation_str=representation_str)
92
+ result = pointer.parse_constraint_pointer()
93
+
94
+ if expected_result is None:
95
+ assert result is None
96
+ else:
97
+ assert result is not None
98
+ assert result.table_name == expected_result.table_name
99
+ assert result.column_names == expected_result.column_names
100
+ assert result.constraint_type == expected_result.constraint_type
101
+
102
+
103
+ @pytest.mark.parametrize(
104
+ "representation_str,expected_table_name",
105
+ [
106
+ # Constraint pointer formats
107
+ ("users.['id'].PRIMARY KEY", "users"),
108
+ ("orders.['user_id', 'product_id'].UNIQUE", "orders"),
109
+ ("public.users.['column'].INDEX", "public.users"),
110
+ # Simple table.column formats
111
+ ("users.email", "users"),
112
+ ("products.name", "products"),
113
+ ("public.users.column", "public.users"), # Schema.table.column format
114
+ # Edge cases
115
+ ("table_only", "table_only"),
116
+ ("", None), # Empty string should return None
117
+ ("users.['id'].PRIMARY KEY", "users"), # Constraint format takes precedence
118
+ # Complex table names with underscores and numbers
119
+ ("user_table_123.column", "user_table_123"),
120
+ ("schema_1.table_2.column", "schema_1.table_2"),
121
+ # Multiple dots in representation (should extract the table part correctly)
122
+ ("very.long.schema.table.['col'].UNIQUE", "very.long.schema.table"),
123
+ ],
124
+ )
125
+ def test_get_table_name(representation_str: str, expected_table_name: str | None):
126
+ """Test extraction of table names from pointer representations."""
127
+ pointer = MockDBObjectPointer(representation_str=representation_str)
128
+ result = pointer.get_table_name()
129
+ assert result == expected_table_name
130
+
131
+
132
+ @pytest.mark.parametrize(
133
+ "representation_str,expected_column_names",
134
+ [
135
+ # Constraint pointer formats
136
+ ("users.['id'].PRIMARY KEY", ["id"]),
137
+ ("orders.['user_id', 'product_id'].UNIQUE", ["user_id", "product_id"]),
138
+ ("products.['name', 'category', 'price'].INDEX", ["name", "category", "price"]),
139
+ ("users.[].CHECK", []), # Empty column list
140
+ # Simple table.column formats
141
+ ("users.email", ["email"]),
142
+ ("products.name", ["name"]),
143
+ ("public.users.column", ["column"]), # Schema.table.column format
144
+ # Edge cases
145
+ ("table_only", []), # No columns
146
+ ("", []), # Empty string
147
+ # Whitespace handling
148
+ ("users.[ 'col1' , 'col2' ].UNIQUE", ["col1", "col2"]),
149
+ # Quote handling
150
+ ("users.[\"col1\", 'col2'].UNIQUE", ["col1", "col2"]),
151
+ # Column names with underscores and numbers
152
+ (
153
+ "users.['user_id_2', 'created_at_timestamp'].UNIQUE",
154
+ ["user_id_2", "created_at_timestamp"],
155
+ ),
156
+ # Complex schema.table.column cases
157
+ ("schema.table.column_name", ["column_name"]),
158
+ ("very.long.schema.table.column", ["column"]),
159
+ ],
160
+ )
161
+ def test_get_column_names(representation_str: str, expected_column_names: list[str]):
162
+ """Test extraction of column names from pointer representations."""
163
+ pointer = MockDBObjectPointer(representation_str=representation_str)
164
+ result = pointer.get_column_names()
165
+ assert result == expected_column_names
2
166
 
3
167
 
4
168
  def test_merge_type_columns():
iceaxe/base.py CHANGED
@@ -273,7 +273,7 @@ class TableBase(BaseModel, metaclass=DBModelMetaclass):
273
273
  :param name: Attribute name
274
274
  :param value: New value
275
275
  """
276
- if name in self.model_fields:
276
+ if name in self.__class__.model_fields:
277
277
  self.modified_attrs[name] = value
278
278
  for callback in self.modified_attrs_callbacks:
279
279
  callback(self)
iceaxe/schemas/actions.py CHANGED
@@ -299,7 +299,7 @@ class DatabaseActions:
299
299
  - Scalar to array types (INTEGER → INTEGER[])
300
300
  - Custom enum conversions (VARCHAR/TEXT → custom enum)
301
301
  - Compatible numeric conversions (INTEGER → BIGINT)
302
-
302
+
303
303
  When autocast=False, PostgreSQL will only allow the type change if it's
304
304
  compatible without explicit casting, which may fail for many conversions.
305
305
 
@@ -308,7 +308,7 @@ class DatabaseActions:
308
308
  await actor.modify_column_type(
309
309
  "products", "price", ColumnType.INTEGER, autocast=True
310
310
  )
311
-
311
+
312
312
  # Manual migration with custom control
313
313
  await actor.modify_column_type(
314
314
  "products", "price", ColumnType.INTEGER, autocast=False
@@ -1,3 +1,4 @@
1
+ import warnings
1
2
  from dataclasses import dataclass
2
3
  from datetime import date, datetime, time, timedelta
3
4
  from inspect import isgenerator
@@ -54,6 +55,47 @@ from iceaxe.typing import (
54
55
  NodeYieldType = Union[DBObject, DBObjectPointer, "NodeDefinition"]
55
56
 
56
57
 
58
+ class CompositePrimaryKeyConstraintError(ValueError):
59
+ """
60
+ Raised when foreign key constraints cannot be resolved due to composite primary keys.
61
+
62
+ This occurs when a table has multiple fields marked as primary_key=True, creating
63
+ a composite primary key constraint, but foreign key constraints expect individual
64
+ primary key constraints on the target columns.
65
+
66
+ """
67
+
68
+ def __init__(self, missing_constraints: list[tuple[str, str]], base_message: str):
69
+ self.missing_constraints = missing_constraints
70
+ self.base_message = base_message
71
+
72
+ # Construct the detailed error message
73
+ error_msg = base_message
74
+
75
+ if missing_constraints:
76
+ error_msg += "\n\nThis error commonly occurs when you have multiple fields marked as primary_key=True in your model."
77
+ error_msg += "\nIceaxe creates a single composite primary key constraint, but foreign key constraints"
78
+ error_msg += (
79
+ "\nexpect individual primary key constraints on the target columns."
80
+ )
81
+ error_msg += "\n\nFor a detailed explanation of why this happens and how to fix it, see:"
82
+ error_msg += "\nhttps://mountaineer.sh/iceaxe/guides/relationships#composite-primary-keys-and-foreign-key-constraints"
83
+ error_msg += "\n\nTo fix this issue, choose one of these approaches:"
84
+ error_msg += "\n\nRecommended: Modify the current table"
85
+ error_msg += (
86
+ "\n - Keep only one field as primary_key=True (e.g., just 'id')"
87
+ )
88
+ error_msg += "\n - Add a UniqueConstraint if you need uniqueness across multiple fields"
89
+ error_msg += "\n - This is usually the better design pattern"
90
+
91
+ # Show specific table/column combinations that are missing
92
+ error_msg += "\n\nCurrently missing individual primary key constraints:"
93
+ for table_name, column_name in missing_constraints:
94
+ error_msg += f"\n - Table '{table_name}' needs a primary key on column '{column_name}'"
95
+
96
+ super().__init__(error_msg)
97
+
98
+
57
99
  @dataclass
58
100
  class NodeDefinition:
59
101
  node: DBObject
@@ -125,9 +167,37 @@ class DatabaseMemorySerializer:
125
167
  pointer.representation() in db_objects_by_name
126
168
  for pointer in dep.pointers
127
169
  ):
128
- raise ValueError(
129
- f"None of the OR pointers {[p.representation() for p in dep.pointers]} found in the defined database objects"
130
- )
170
+ # Create a more helpful error message for common cases
171
+ missing_pointers = [
172
+ p.representation() for p in dep.pointers
173
+ ]
174
+ error_msg = f"None of the OR pointers {missing_pointers} found in the defined database objects"
175
+
176
+ # Check if this is the common case of multiple primary keys causing foreign key issues
177
+ primary_key_pointers = []
178
+ for p in dep.pointers:
179
+ parsed = p.parse_constraint_pointer()
180
+ if parsed and parsed.constraint_type == "PRIMARY KEY":
181
+ primary_key_pointers.append(p)
182
+
183
+ if primary_key_pointers:
184
+ # Extract table and column info from the primary key pointers
185
+ primary_key_info: list[tuple[str, str]] = []
186
+ for pointer in primary_key_pointers:
187
+ table_name = pointer.get_table_name()
188
+ column_names = pointer.get_column_names()
189
+
190
+ if table_name and column_names:
191
+ for column_name in column_names:
192
+ primary_key_info.append(
193
+ (table_name, column_name)
194
+ )
195
+
196
+ if primary_key_info:
197
+ raise CompositePrimaryKeyConstraintError(
198
+ primary_key_info, error_msg
199
+ )
200
+ raise ValueError(error_msg)
131
201
  elif dep.representation() not in db_objects_by_name:
132
202
  raise ValueError(
133
203
  f"Pointer {dep.representation()} not found in the defined database objects"
@@ -544,6 +614,18 @@ class DatabaseHandler:
544
614
  if not keys:
545
615
  return
546
616
 
617
+ # Warn users about potential issues with multiple primary keys
618
+ if len(keys) > 1:
619
+ column_names = [key for key, _ in keys]
620
+ warnings.warn(
621
+ f"Table '{table.get_table_name()}' has multiple fields marked as primary_key=True: {column_names}. "
622
+ f"This creates a composite primary key constraint, which may cause issues with foreign key "
623
+ f"constraints that expect individual primary keys on target columns. "
624
+ f"Consider using only one primary key field and adding UniqueConstraint for uniqueness instead.",
625
+ UserWarning,
626
+ stacklevel=3,
627
+ )
628
+
547
629
  columns = [key for key, _ in keys]
548
630
  yield from self._yield_nodes(
549
631
  DBConstraint(
@@ -1,4 +1,6 @@
1
+ import re
1
2
  from abc import abstractmethod
3
+ from dataclasses import dataclass
2
4
  from typing import Self, Union
3
5
 
4
6
  from pydantic import BaseModel, Field, model_validator
@@ -12,6 +14,15 @@ from iceaxe.schemas.actions import (
12
14
  )
13
15
 
14
16
 
17
+ @dataclass
18
+ class ConstraintPointerInfo:
19
+ """Information parsed from a constraint pointer representation."""
20
+
21
+ table_name: str
22
+ column_names: list[str]
23
+ constraint_type: str
24
+
25
+
15
26
  class DBObject(BaseModel):
16
27
  """
17
28
  A subclass for all models that are intended to store
@@ -86,6 +97,108 @@ class DBObjectPointer(BaseModel):
86
97
  def representation(self) -> str:
87
98
  pass
88
99
 
100
+ def parse_constraint_pointer(self) -> ConstraintPointerInfo | None:
101
+ """
102
+ Parse a constraint pointer representation into its components.
103
+
104
+ Returns:
105
+ ConstraintPointerInfo | None: Parsed constraint information or None if not a constraint pointer
106
+
107
+ Examples:
108
+ "table.['column'].PRIMARY KEY" -> ConstraintPointerInfo("table", ["column"], "PRIMARY KEY")
109
+ "table.['col1', 'col2'].UNIQUE" -> ConstraintPointerInfo("table", ["col1", "col2"], "UNIQUE")
110
+ """
111
+ representation = self.representation()
112
+
113
+ # Pattern to match: table_name.[column_list].constraint_type
114
+ # where column_list can be ['col'] or ['col1', 'col2', ...]
115
+ # The table_name can contain dots (for schema.table), so we need to be more careful
116
+ # We look for the pattern .[...]. to identify where the column list starts
117
+ pattern = r"^(.+)\.(\[.*?\])\.(.+)$"
118
+ match = re.match(pattern, representation)
119
+
120
+ if not match:
121
+ return None
122
+
123
+ table_name, columns_part, constraint_type = match.groups()
124
+
125
+ # Validate that the column list contains properly quoted column names or is empty
126
+ # Remove brackets and check the content
127
+ columns_str = columns_part.strip("[]")
128
+ if not columns_str:
129
+ # Empty column list is valid
130
+ return ConstraintPointerInfo(table_name, [], constraint_type)
131
+
132
+ # Split by comma and validate each column name is properly quoted
133
+ columns = []
134
+ for col in columns_str.split(","):
135
+ col = col.strip()
136
+ # Check if the column is properly quoted (single or double quotes)
137
+ if (col.startswith("'") and col.endswith("'")) or (
138
+ col.startswith('"') and col.endswith('"')
139
+ ):
140
+ # Remove quotes and add to list
141
+ col_name = col[1:-1]
142
+ if col_name: # Don't add empty column names
143
+ columns.append(col_name)
144
+ else:
145
+ # Column is not properly quoted, this is not a valid constraint pointer
146
+ return None
147
+
148
+ return ConstraintPointerInfo(table_name, columns, constraint_type)
149
+
150
+ def get_table_name(self) -> str | None:
151
+ """
152
+ Extract the table name from the pointer representation.
153
+
154
+ Returns:
155
+ str | None: The table name if it can be parsed, None otherwise
156
+ """
157
+ # Try constraint pointer format first
158
+ parsed = self.parse_constraint_pointer()
159
+ if parsed is not None:
160
+ return parsed.table_name
161
+
162
+ # Try simple table.column format
163
+ representation = self.representation()
164
+ if not representation:
165
+ return None
166
+
167
+ parts = representation.split(".")
168
+ if len(parts) >= 2:
169
+ # For schema.table.column format, take all parts except the last one
170
+ return ".".join(parts[:-1])
171
+ elif len(parts) == 1:
172
+ # Just a table name
173
+ return parts[0]
174
+ else:
175
+ return None
176
+
177
+ def get_column_names(self) -> list[str]:
178
+ """
179
+ Extract column names from the pointer representation.
180
+
181
+ Returns:
182
+ list[str]: List of column names if they can be parsed, empty list otherwise
183
+ """
184
+ # Try constraint pointer format first
185
+ parsed = self.parse_constraint_pointer()
186
+ if parsed is not None:
187
+ return parsed.column_names
188
+
189
+ # Try simple table.column format
190
+ representation = self.representation()
191
+ if not representation:
192
+ return []
193
+
194
+ parts = representation.split(".")
195
+ if len(parts) >= 2:
196
+ # For schema.table.column format, take the last part as the column name
197
+ return [parts[-1]]
198
+ else:
199
+ # Just a table name, no columns
200
+ return []
201
+
89
202
 
90
203
  class DBTable(DBObject):
91
204
  table_name: str
iceaxe/session.py CHANGED
@@ -593,7 +593,7 @@ class DBConnection:
593
593
  modified_attrs = frozenset(
594
594
  k
595
595
  for k, v in obj.get_modified_attributes().items()
596
- if not obj.model_fields[k].exclude
596
+ if not obj.__class__.model_fields[k].exclude
597
597
  )
598
598
  if modified_attrs:
599
599
  updates_by_fields[modified_attrs].append(obj)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iceaxe
3
- Version: 0.7.0.dev1
3
+ Version: 0.7.0.dev2
4
4
  Summary: A modern, fast ORM for Python.
5
5
  Author-email: Pierce Freeman <pierce@freeman.vc>
6
6
  Requires-Python: >=3.11
@@ -15,8 +15,7 @@ Dynamic: license-file
15
15
 
16
16
  ![Iceaxe Logo](https://raw.githubusercontent.com/piercefreeman/iceaxe/main/media/header.png)
17
17
 
18
- ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/iceaxe)
19
- [![Test status](https://github.com/piercefreeman/iceaxe/actions/workflows/test.yml/badge.svg)](https://github.com/piercefreeman/iceaxe/actions)
18
+ ![Python Version](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fpiercefreeman%2Ficeaxe%2Frefs%2Fheads%2Fmain%2Fpyproject.toml) [![Test status](https://github.com/piercefreeman/iceaxe/actions/workflows/test.yml/badge.svg)](https://github.com/piercefreeman/iceaxe/actions)
20
19
 
21
20
  A modern, fast ORM for Python. We have the following goals:
22
21
 
@@ -5,7 +5,7 @@ iceaxe/sql_types.py,sha256=fEdDPeb7QctFfdWEn1rLqY1b2EccM61UEAaqXdyz3jo,2607
5
5
  iceaxe/alias_values.py,sha256=gvtaYJLdMkSUBS99GlFGDMul4jlOs_lxnmgRae7OrKI,1978
6
6
  iceaxe/session_optimized.pyx,sha256=5sPvWKuEZKzwaOFLE4knU8kRVWt0JCVTugfQCTpRPmo,8375
7
7
  iceaxe/io.py,sha256=af-ZJKiP0yOtLCe2dG0jMOBffbAvZ1BMSHG6lVjQ1Bc,3889
8
- iceaxe/session.py,sha256=clEyfi-Wa0GIzZgXdKbwwzEeTzGvDRKTiQ4-oFFCj0g,32606
8
+ iceaxe/session.py,sha256=0EFbCHA2XBiMS7IT6Cb0-HLE0Q-t2nsWXbe9DdvKyQQ,32616
9
9
  iceaxe/__init__.py,sha256=zIq6C5rZY3ERBeOfZBIInM78RvR35ub5oKVw_jj1VO0,614
10
10
  iceaxe/modifications.py,sha256=xTs3pdOpeVm_lwKL0DkHVm9a3Kn-HIJtUTTNEwpcUrs,6588
11
11
  iceaxe/comparison.py,sha256=fpXZHQrVirTiSox6vpYvTnWJjGJyuZGoEu-98YmuK2g,17005
@@ -16,7 +16,7 @@ iceaxe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  iceaxe/queries.py,sha256=ZPiJYG_hyRWSUjaC7-gfscr2h4GbGaNT67uyjaScr4A,44461
17
17
  iceaxe/typing.py,sha256=s1rCCE2Pcs5Fxt1zY76UppTc9LiXyydzksTm_k2BUmw,1917
18
18
  iceaxe/generics.py,sha256=TGI4EIshzwwRoVi1eoux0Dn64xcWvbErSyxd_wK-iHc,4891
19
- iceaxe/base.py,sha256=_nmpyyoSU1qQb62vZIkws_Pum0XvEfsocIVO8_-NnHA,11377
19
+ iceaxe/base.py,sha256=o2c3RIqUnoK1SKB7mkEetcKKdPMtTHBvOawGYCm8usM,11387
20
20
  iceaxe/session_optimized.cpython-312-darwin.so,sha256=pm_UfdLXczkxclLYL1B5ATTNOaGBbLe0Xxa6UfQ76Xg,171320
21
21
  iceaxe/mountaineer/config.py,sha256=sdzNbrXkLPw1W3RUE2Gsq1m076lvjhw9wGxX1AHEWbw,1404
22
22
  iceaxe/mountaineer/__init__.py,sha256=psc2Tj25KHoqmL9kCatZ31Y96ilamERI2Rn0dqQX0zM,298
@@ -30,12 +30,12 @@ iceaxe/migrations/cli.py,sha256=FzejSzE1n5FOkhtyGXLuRKWOTR4DlP6xTPxdqqzTCC0,8802
30
30
  iceaxe/migrations/client_io.py,sha256=f-yJJaaB4yfNlyHk2ou9eKrWIiSzB11rIoSlhDXwQLo,2314
31
31
  iceaxe/migrations/migrator.py,sha256=Z1-baX2qAvFaAVYC2oFv0VkFHO6XZRYYqKz9wRke8yo,3038
32
32
  iceaxe/migrations/migration.py,sha256=HYtFx_AlmUQF1RJFulAFKaM2XLS1NmynufvjUbTJBec,2370
33
- iceaxe/schemas/actions.py,sha256=PfEjfSAUiiNf4LjGHSLoLJxFT5IDYtPwqTNxJKOcaIE,30082
33
+ iceaxe/schemas/actions.py,sha256=70aHXmWkJx2E4emH1na5OET-m8rPIdEvCUkL1mvB3Ps,30046
34
34
  iceaxe/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  iceaxe/schemas/db_serializer.py,sha256=SpMxo_f66R3rKl7uEiRIwS5ZoBCe9ETIz4R__KBT5fA,13070
36
36
  iceaxe/schemas/cli.py,sha256=G-ySDV1sIvqDLln23Xc6eKlEQw5-ZydVAzFPgGQGeo4,1019
37
- iceaxe/schemas/db_stubs.py,sha256=cOzRmFVBl70DVO9IQ7iL2d-mefkXjy1c0lNDCtiKcAA,13746
38
- iceaxe/schemas/db_memory_serializer.py,sha256=FvBvxgJB3diWafsd5832Tmm7CekJK9AEAQa06vn_TDc,24623
37
+ iceaxe/schemas/db_stubs.py,sha256=B2JdnrChqKUMZwNPAtzVkC2bHQfoN_bFczL-mZZy4_A,17849
38
+ iceaxe/schemas/db_memory_serializer.py,sha256=44jf2eI2YMuQpS3XB1UxIl43u1pv8EOZXbjZ1M-O5GY,29079
39
39
  iceaxe/__tests__/conftest.py,sha256=S_leF61hosjmXjYnvammgKckdl8w70LiwHJQzU7i2Us,4010
40
40
  iceaxe/__tests__/test_session.py,sha256=ZuUe7B_H-aW4Ym3Wx7WhBDhcyIlE7jMcpeD1jOkYyJs,49199
41
41
  iceaxe/__tests__/test_modifications.py,sha256=beljqGGTc-Mg4sogyvUSNCOYp57AjNRi41e-vdMqMOM,5061
@@ -59,16 +59,16 @@ iceaxe/__tests__/migrations/test_generator.py,sha256=Z9GlZCCWwbysUO-y3S6R76PodY_
59
59
  iceaxe/__tests__/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
60
  iceaxe/__tests__/migrations/test_generics.py,sha256=JuaniWOsfQasGF-pPx7pyyXEuNO9sfFtQaIfGntqNCQ,2654
61
61
  iceaxe/__tests__/schemas/test_actions.py,sha256=-lM3Kz1R0_hJsQNd3A0NGe83cFPuSpcgMS5Z9yIRGmU,38340
62
- iceaxe/__tests__/schemas/test_db_memory_serializer.py,sha256=jU1FWH3DUL_hnTo_yOgVzVi9N_xuMPsMYov6jY88aCg,44940
62
+ iceaxe/__tests__/schemas/test_db_memory_serializer.py,sha256=6zAIuY-K4XKzLSHaCUq_UE3sa3h66hPzl3BPlF-nMVE,47103
63
63
  iceaxe/__tests__/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
- iceaxe/__tests__/schemas/test_db_stubs.py,sha256=-8Pkcy8QUSJFl0nZdg_uuyJTRPeZWdWAdrlLwt4nFxE,787
64
+ iceaxe/__tests__/schemas/test_db_stubs.py,sha256=Bh_bPeKvz9bQGM6-03iqs2PXS2Ik_klzO508fHzm-wE,7456
65
65
  iceaxe/__tests__/schemas/test_db_serializer.py,sha256=InW5x51f73Zgdw8KDK6G0hUbi64FYzVbu8TmFa0VH8g,10752
66
66
  iceaxe/__tests__/schemas/test_cli.py,sha256=mQe0GKGPwwJ3foJCEDy4N-uERfjI_eU1QLDk0AaT588,738
67
67
  iceaxe/__tests__/benchmarks/test_bulk_insert.py,sha256=XsMsmGD0y4BQGXKvciPZ_cW1WzYjqW7rKyo2XfgfDjA,1284
68
68
  iceaxe/__tests__/benchmarks/test_select.py,sha256=ZyIDvuJA0j4g3kMnoF4eaxO4D-f0BAxznpZMfkETQU0,3984
69
69
  iceaxe/__tests__/benchmarks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
- iceaxe-0.7.0.dev1.dist-info/RECORD,,
71
- iceaxe-0.7.0.dev1.dist-info/WHEEL,sha256=CltXN3lQvXbHxKDtiDwW0RNzF8s2WyBuPbOAX_ZeQlA,109
72
- iceaxe-0.7.0.dev1.dist-info/top_level.txt,sha256=pTQaTX1areyLx9BHkSfYqSEU0r6dHJMxjxMme0CK9xw,7
73
- iceaxe-0.7.0.dev1.dist-info/METADATA,sha256=bpHO4E7z0tGeeKD5WQGP7guvtGYRrQKAquCeowEFncI,9032
74
- iceaxe-0.7.0.dev1.dist-info/licenses/LICENSE,sha256=DgU6htH6BJg7h_6609WHBMKEUN_Y04BDN_vTTrmp5Pg,1071
70
+ iceaxe-0.7.0.dev2.dist-info/RECORD,,
71
+ iceaxe-0.7.0.dev2.dist-info/WHEEL,sha256=CltXN3lQvXbHxKDtiDwW0RNzF8s2WyBuPbOAX_ZeQlA,109
72
+ iceaxe-0.7.0.dev2.dist-info/top_level.txt,sha256=pTQaTX1areyLx9BHkSfYqSEU0r6dHJMxjxMme0CK9xw,7
73
+ iceaxe-0.7.0.dev2.dist-info/METADATA,sha256=IFs9P6ZHyIdws_g7gHNs28I3DH1d2OrGAfCeqId20KQ,9148
74
+ iceaxe-0.7.0.dev2.dist-info/licenses/LICENSE,sha256=DgU6htH6BJg7h_6609WHBMKEUN_Y04BDN_vTTrmp5Pg,1071