sqlobjects 1.1.0__tar.gz → 1.2.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 (77) hide show
  1. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/CHANGELOG.md +6 -0
  2. {sqlobjects-1.1.0/sqlobjects.egg-info → sqlobjects-1.2.0}/PKG-INFO +1 -1
  3. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/pyproject.toml +10 -10
  4. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/metadata.py +33 -12
  5. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/session.py +17 -3
  6. {sqlobjects-1.1.0 → sqlobjects-1.2.0/sqlobjects.egg-info}/PKG-INFO +1 -1
  7. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/LICENSE +0 -0
  8. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/README.md +0 -0
  9. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/docs/rules/01-database-session-guide.md +0 -0
  10. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/docs/rules/02-model-definition-guide.md +0 -0
  11. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/docs/rules/03-query-operations-guide.md +0 -0
  12. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/docs/rules/04-crud-operations-guide.md +0 -0
  13. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/docs/rules/05-relationships-guide.md +0 -0
  14. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/docs/rules/06-validation-signals-guide.md +0 -0
  15. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/docs/rules/07-performance-guide.md +0 -0
  16. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/docs/rules/README.md +0 -0
  17. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/setup.cfg +0 -0
  18. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/__init__.py +0 -0
  19. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/_install_rules.py +0 -0
  20. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/cascade.py +0 -0
  21. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/database/__init__.py +0 -0
  22. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/database/config.py +0 -0
  23. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/database/manager.py +0 -0
  24. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/exceptions.py +0 -0
  25. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/expressions/__init__.py +0 -0
  26. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/expressions/aggregate.py +0 -0
  27. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/expressions/base.py +0 -0
  28. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/expressions/cte.py +0 -0
  29. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/expressions/explain.py +0 -0
  30. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/expressions/function.py +0 -0
  31. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/expressions/mixins.py +0 -0
  32. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/expressions/scalar.py +0 -0
  33. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/expressions/subquery.py +0 -0
  34. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/expressions/terminal.py +0 -0
  35. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/expressions/window.py +0 -0
  36. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/__init__.py +0 -0
  37. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/core.py +0 -0
  38. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/functions.py +0 -0
  39. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/proxies.py +0 -0
  40. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/relations/__init__.py +0 -0
  41. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/relations/descriptors.py +0 -0
  42. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/relations/managers.py +0 -0
  43. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/relations/prefetch.py +0 -0
  44. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/relations/strategies.py +0 -0
  45. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/relations/utils.py +0 -0
  46. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/shortcuts.py +0 -0
  47. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/types/__init__.py +0 -0
  48. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/types/base.py +0 -0
  49. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/types/comparators.py +0 -0
  50. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/types/registry.py +0 -0
  51. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/fields/utils.py +0 -0
  52. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/internal/__init__.py +0 -0
  53. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/internal/operations.py +0 -0
  54. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/internal/results.py +0 -0
  55. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/mixins.py +0 -0
  56. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/model.py +0 -0
  57. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/objects/__init__.py +0 -0
  58. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/objects/bulk.py +0 -0
  59. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/objects/core.py +0 -0
  60. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/objects/upsert.py +0 -0
  61. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/queries/__init__.py +0 -0
  62. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/queries/builder.py +0 -0
  63. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/queries/dialect.py +0 -0
  64. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/queries/executor.py +0 -0
  65. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/queryset.py +0 -0
  66. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/signals.py +0 -0
  67. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/utils/__init__.py +0 -0
  68. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/utils/inspect.py +0 -0
  69. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/utils/naming.py +0 -0
  70. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/utils/pattern.py +0 -0
  71. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects/validators.py +0 -0
  72. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects.egg-info/SOURCES.txt +0 -0
  73. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects.egg-info/dependency_links.txt +0 -0
  74. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects.egg-info/entry_points.txt +0 -0
  75. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects.egg-info/requires.txt +0 -0
  76. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/sqlobjects.egg-info/top_level.txt +0 -0
  77. {sqlobjects-1.1.0 → sqlobjects-1.2.0}/tests/test_config.py +0 -0
@@ -1,3 +1,9 @@
1
+ ## 1.2.0 (2026-02-25)
2
+
3
+ ### Feat
4
+
5
+ - **metadata**: improve constraint and index naming conventions
6
+
1
7
  ## 1.1.0 (2026-02-14)
2
8
 
3
9
  ### Feat
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlobjects
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: Django-style async ORM library based on SQLAlchemy with chainable queries, Q objects, and relationship loading
5
5
  Author-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
6
6
  Maintainer-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sqlobjects"
3
- version = "1.1.0"
3
+ version = "1.2.0"
4
4
  description = "Django-style async ORM library based on SQLAlchemy with chainable queries, Q objects, and relationship loading"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -49,22 +49,22 @@ Changelog = "https://github.com/XtraVisionsAI/sqlobjects/blob/main/CHANGELOG.md"
49
49
 
50
50
  [dependency-groups]
51
51
  dev = [
52
- "pre-commit>=4.4.0",
53
- "pyright>=1.1.407",
54
- "ruff>=0.14.5",
55
- "setuptools>=80.9.0",
52
+ "pre-commit>=4.5.1",
53
+ "pyright>=1.1.408",
54
+ "ruff>=0.15.2",
55
+ "setuptools>=82.0.0",
56
56
  ]
57
57
  test = [
58
58
  "aiomysql>=0.3.2",
59
- "aiosqlite>=0.20.0",
60
- "asyncpg>=0.30.0",
61
- "pytest>=9.0.1",
59
+ "aiosqlite>=0.22.1",
60
+ "asyncpg>=0.31.0",
61
+ "pytest>=9.0.2",
62
62
  "pytest-asyncio>=1.3.0",
63
- "psutil>=7.1.3", # For memory monitoring in performance tests
63
+ "psutil>=7.2.2", # For memory monitoring in performance tests
64
64
  ]
65
65
 
66
66
  [build-system]
67
- requires = ["setuptools>=61.0", "wheel"]
67
+ requires = ["setuptools>=82.0.0", "wheel"]
68
68
  build-backend = "setuptools.build_meta"
69
69
 
70
70
  [tool.setuptools]
@@ -2,7 +2,7 @@ import re
2
2
  from dataclasses import dataclass, field
3
3
  from typing import TYPE_CHECKING, Any, Union, cast
4
4
 
5
- from sqlalchemy import CheckConstraint, Index, UniqueConstraint
5
+ from sqlalchemy import CheckConstraint, ForeignKeyConstraint, Index, UniqueConstraint
6
6
  from sqlalchemy import MetaData as SqlAlchemyMetaData
7
7
 
8
8
  from .fields import ColumnAttribute
@@ -24,8 +24,8 @@ __all__ = [
24
24
  "unique",
25
25
  ]
26
26
 
27
-
28
27
  _FIELD_NAME_PATTERN = re.compile(r"\b([a-zA-Z_][a-zA-Z0-9_]*)\b")
28
+ _TEMP_INDEX_PREFIX = "__temp__idx_"
29
29
 
30
30
 
31
31
  @dataclass
@@ -574,7 +574,9 @@ class ModelProcessor(type):
574
574
 
575
575
  @classmethod
576
576
  def _normalize_all_indexes(mcs, indexes: list[Index], table_name: str) -> list[Index]:
577
- """Force uniform naming format for all indexes.
577
+ """Normalize index names that need normalization.
578
+
579
+ Only normalizes indexes with temporary names (starting with __temp__idx_).
578
580
 
579
581
  Args:
580
582
  indexes: List of indexes to normalize
@@ -586,6 +588,11 @@ class ModelProcessor(type):
586
588
  normalized_indexes = []
587
589
 
588
590
  for idx in indexes:
591
+ # Only normalize temporary names
592
+ if not idx.name or not idx.name.startswith(_TEMP_INDEX_PREFIX):
593
+ normalized_indexes.append(idx)
594
+ continue
595
+
589
596
  # Get field name list
590
597
  if hasattr(idx, "columns") and idx.columns:
591
598
  field_names = "_".join(col.name for col in idx.columns) # noqa
@@ -597,10 +604,7 @@ class ModelProcessor(type):
597
604
  continue
598
605
 
599
606
  # Generate standardized name
600
- new_name = f"idx_{table_name}_{field_names}"
601
-
602
- # Directly modify index name (instead of rebuilding)
603
- idx.name = new_name # type: ignore[reportAttributeAccessIssue]
607
+ idx.name = f"idx_{table_name}_{field_names}" # type: ignore[reportAttributeAccessIssue]
604
608
  normalized_indexes.append(idx)
605
609
 
606
610
  return normalized_indexes
@@ -683,15 +687,20 @@ class ModelProcessor(type):
683
687
  def _post_process_table_indexes(mcs, table, table_name: str) -> None:
684
688
  """Normalize index names after table construction.
685
689
 
690
+ Only normalizes indexes with temporary names (starting with __temp__idx_).
691
+
686
692
  Args:
687
693
  table: SQLAlchemy Table instance
688
694
  table_name: Database table name
689
695
  """
690
696
  for idx in table.indexes:
697
+ # Only normalize temporary names
698
+ if not idx.name or not idx.name.startswith(_TEMP_INDEX_PREFIX):
699
+ continue
700
+
691
701
  if hasattr(idx, "columns") and idx.columns:
692
702
  field_names = "_".join(col.name for col in idx.columns)
693
- new_name = f"idx_{table_name}_{field_names}"
694
- idx.name = new_name
703
+ idx.name = f"idx_{table_name}_{field_names}"
695
704
 
696
705
  @classmethod
697
706
  def _post_process_table_constraints(mcs, table, table_name: str) -> None:
@@ -714,6 +723,20 @@ class ModelProcessor(type):
714
723
  elif isinstance(cst, UniqueConstraint) and hasattr(cst, "columns"):
715
724
  field_names = "_".join(col.name for col in cst.columns)
716
725
  cst.name = f"uq_{table_name}_{field_names}"
726
+ elif isinstance(cst, ForeignKeyConstraint) and hasattr(cst, "columns"):
727
+ # Handle foreign key constraints
728
+ field_names = "_".join(col.name for col in cst.columns)
729
+ # Get referenced table and column names
730
+ if cst.elements:
731
+ try:
732
+ ref_table = cst.elements[0].column.table.name
733
+ ref_columns = "_".join(elem.column.name for elem in cst.elements)
734
+ cst.name = f"fk_{table_name}_{field_names}_{ref_table}_{ref_columns}"
735
+ except Exception:
736
+ # Fallback if reference cannot be resolved yet
737
+ cst.name = f"fk_{table_name}_{field_names}"
738
+ else:
739
+ cst.name = f"fk_{table_name}_{field_names}"
717
740
 
718
741
  @classmethod
719
742
  def _apply_dataclass_functionality(mcs, cls: Any) -> Any:
@@ -1114,11 +1137,9 @@ def index(
1114
1137
  >>> index("idx_users_status", "status", postgresql_where="status = 'active'")
1115
1138
  >>> index("idx_users_tags", "tags", postgresql_using="gin")
1116
1139
  """
1117
- # Note: Don't auto-generate name here because table_name is needed
1118
- # Actual name normalization is handled in _merge_indexes
1119
1140
  if name is None:
1120
1141
  field_part = "_".join(fields)
1121
- name = f"idx_{field_part}" # Temporary name, will be replaced later
1142
+ name = f"{_TEMP_INDEX_PREFIX}{field_part}"
1122
1143
 
1123
1144
  # Build dialect-specific kwargs
1124
1145
  dialect_kwargs = {}
@@ -3,7 +3,7 @@ from collections.abc import AsyncGenerator
3
3
  from contextlib import asynccontextmanager
4
4
  from typing import Any
5
5
 
6
- from sqlalchemy import CursorResult
6
+ from sqlalchemy import CursorResult, text
7
7
  from sqlalchemy.exc import SQLAlchemyError
8
8
  from sqlalchemy.ext.asyncio import AsyncConnection, AsyncResult
9
9
 
@@ -13,7 +13,6 @@ from .exceptions import convert_sqlalchemy_error
13
13
 
14
14
  __all__ = ["AsyncSession", "ctx_session", "ctx_sessions", "get_session", "has_session"]
15
15
 
16
-
17
16
  # Explicit session management (highest priority)
18
17
  _explicit_sessions: contextvars.ContextVar[dict[str, "AsyncSession"]] = contextvars.ContextVar("explicit_sessions")
19
18
 
@@ -64,9 +63,17 @@ class AsyncSession:
64
63
  return get_database(self._db_name).engine
65
64
 
66
65
  async def execute(self, statement: Any, parameters: Any = None) -> CursorResult[Any]:
67
- """Execute statement with automatic transaction management."""
66
+ """Execute statement with automatic transaction management.
67
+
68
+ Supports both SQLAlchemy statement objects and raw SQL strings.
69
+ Raw SQL strings are automatically wrapped with text().
70
+ """
68
71
  await self._ensure_connection()
69
72
 
73
+ # Auto-wrap string SQL with text()
74
+ if isinstance(statement, str):
75
+ statement = text(statement)
76
+
70
77
  # Auto-begin transaction for non-readonly sessions
71
78
  if not self.readonly and self._trans is None:
72
79
  self._trans = await self._conn.begin() # type: ignore
@@ -89,6 +96,9 @@ class AsyncSession:
89
96
  async def stream(self, statement: Any, parameters: Any = None) -> AsyncResult[Any]:
90
97
  """Execute statement and return streaming result.
91
98
 
99
+ Supports both SQLAlchemy statement objects and raw SQL strings.
100
+ Raw SQL strings are automatically wrapped with text().
101
+
92
102
  Note: stream() is not supported with auto_commit=True sessions.
93
103
  Use explicit sessions (ctx_session) for streaming operations.
94
104
  """
@@ -99,6 +109,10 @@ class AsyncSession:
99
109
 
100
110
  await self._ensure_connection()
101
111
 
112
+ # Auto-wrap string SQL with text()
113
+ if isinstance(statement, str):
114
+ statement = text(statement)
115
+
102
116
  # Auto-begin transaction for non-readonly sessions
103
117
  if not self.readonly and self._trans is None:
104
118
  self._trans = await self._conn.begin() # type: ignore
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlobjects
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: Django-style async ORM library based on SQLAlchemy with chainable queries, Q objects, and relationship loading
5
5
  Author-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
6
6
  Maintainer-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
File without changes
File without changes
File without changes