ic-python-db 0.7.4__tar.gz → 0.7.6__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.
- {ic_python_db-0.7.4/ic_python_db.egg-info → ic_python_db-0.7.6}/PKG-INFO +1 -1
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/ic_python_db/__init__.py +1 -1
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/ic_python_db/entity.py +78 -28
- {ic_python_db-0.7.4 → ic_python_db-0.7.6/ic_python_db.egg-info}/PKG-INFO +1 -1
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/pyproject.toml +1 -1
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/setup.py +1 -1
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/LICENSE +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/MANIFEST.in +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/README.md +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/ic_python_db/_cdk.py +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/ic_python_db/constants.py +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/ic_python_db/context.py +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/ic_python_db/db_engine.py +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/ic_python_db/hooks.py +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/ic_python_db/mixins.py +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/ic_python_db/properties.py +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/ic_python_db/py.typed +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/ic_python_db/storage.py +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/ic_python_db/system_time.py +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/ic_python_db.egg-info/SOURCES.txt +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/ic_python_db.egg-info/dependency_links.txt +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/ic_python_db.egg-info/top_level.txt +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/requirements-dev.txt +0 -0
- {ic_python_db-0.7.4 → ic_python_db-0.7.6}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ic_python_db
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.6
|
|
4
4
|
Summary: A lightweight key-value database written in Python, intended for use on the Internet Computer (IC)
|
|
5
5
|
Home-page: https://github.com/smart-social-contracts/ic-python-db
|
|
6
6
|
Author: Smart Social Contracts
|
|
@@ -180,7 +180,8 @@ class Entity:
|
|
|
180
180
|
setattr(self, k, v)
|
|
181
181
|
self._do_not_save = False
|
|
182
182
|
|
|
183
|
-
self.
|
|
183
|
+
if not self._loaded:
|
|
184
|
+
self._save()
|
|
184
185
|
|
|
185
186
|
@classmethod
|
|
186
187
|
def new(cls, **kwargs):
|
|
@@ -263,8 +264,8 @@ class Entity:
|
|
|
263
264
|
)
|
|
264
265
|
self._update_timestamps(caller_id)
|
|
265
266
|
|
|
266
|
-
# Save to database
|
|
267
|
-
data = self.
|
|
267
|
+
# Save to database (full serialization preserves all relations)
|
|
268
|
+
data = self._serialize_full()
|
|
268
269
|
|
|
269
270
|
if not self._do_not_save:
|
|
270
271
|
logger.debug(f"Saving entity {self._type}@{self._id} to database")
|
|
@@ -386,6 +387,10 @@ class Entity:
|
|
|
386
387
|
# Create instance first
|
|
387
388
|
entity = cls(**data, _loaded=True)
|
|
388
389
|
|
|
390
|
+
# If migration was applied, persist the migrated data
|
|
391
|
+
if stored_version != current_version:
|
|
392
|
+
entity._save()
|
|
393
|
+
|
|
389
394
|
# Restore legacy "relations" block if present in serialized data.
|
|
390
395
|
# Otherwise keep the _relations that __init__ already populated
|
|
391
396
|
# via relationship descriptors (OneToMany, ManyToOne, etc.).
|
|
@@ -506,9 +511,14 @@ class Entity:
|
|
|
506
511
|
|
|
507
512
|
while len(ret) < count and from_id <= cls.max_id():
|
|
508
513
|
logger.info(f"Loading entity {from_id}")
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
514
|
+
try:
|
|
515
|
+
entity = cls.load(str(from_id))
|
|
516
|
+
if entity:
|
|
517
|
+
ret.append(entity)
|
|
518
|
+
except (ValueError, AttributeError) as e:
|
|
519
|
+
# Skip entities with broken/dangling relation references
|
|
520
|
+
# (full fix: issue #4 — lazy relation resolution)
|
|
521
|
+
logger.warning(f"Skipping {cls.__name__}@{from_id}: {e}")
|
|
512
522
|
from_id += 1
|
|
513
523
|
|
|
514
524
|
return ret
|
|
@@ -554,19 +564,15 @@ class Entity:
|
|
|
554
564
|
# Remove from context
|
|
555
565
|
self.__class__._context.discard(self)
|
|
556
566
|
|
|
557
|
-
def
|
|
558
|
-
"""
|
|
559
|
-
|
|
560
|
-
Returns:
|
|
561
|
-
Dict containing the entity's serializable data
|
|
562
|
-
"""
|
|
567
|
+
def _serialize_base(self) -> Dict[str, Any]:
|
|
568
|
+
"""Shared serialization logic: core data, properties, and instance attributes."""
|
|
563
569
|
# Get mixin data first if available
|
|
564
570
|
data = super().serialize() if hasattr(super(), "serialize") else {}
|
|
565
571
|
|
|
566
572
|
# Add core entity data
|
|
567
573
|
data.update(
|
|
568
574
|
{
|
|
569
|
-
"_type": self._type,
|
|
575
|
+
"_type": self._type,
|
|
570
576
|
"_id": self._id,
|
|
571
577
|
}
|
|
572
578
|
)
|
|
@@ -584,30 +590,74 @@ class Entity:
|
|
|
584
590
|
if not k.startswith("_"):
|
|
585
591
|
data[k] = v
|
|
586
592
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
593
|
+
return data
|
|
594
|
+
|
|
595
|
+
@staticmethod
|
|
596
|
+
def _get_entity_reference(entity):
|
|
597
|
+
"""Get the best reference for an entity: alias value if available, otherwise _id."""
|
|
598
|
+
if hasattr(entity.__class__, "__alias__") and entity.__class__.__alias__:
|
|
599
|
+
alias_field = entity.__class__.__alias__
|
|
600
|
+
alias_value = getattr(entity, alias_field, None)
|
|
601
|
+
if alias_value is not None:
|
|
602
|
+
return alias_value
|
|
603
|
+
return entity._id
|
|
604
|
+
|
|
605
|
+
def _serialize_full(self) -> Dict[str, Any]:
|
|
606
|
+
"""Full serialization including all relations. Used by _save() for persistence."""
|
|
607
|
+
data = self._serialize_base()
|
|
608
|
+
|
|
609
|
+
from ic_python_db.properties import ManyToMany, OneToMany
|
|
610
|
+
|
|
611
|
+
for rel_name, rel_entities in self._relations.items():
|
|
612
|
+
if rel_entities:
|
|
613
|
+
rel_prop = getattr(self.__class__, rel_name, None)
|
|
614
|
+
is_to_many = isinstance(rel_prop, (OneToMany, ManyToMany))
|
|
615
|
+
|
|
616
|
+
if len(rel_entities) == 1 and not is_to_many:
|
|
617
|
+
data[rel_name] = self._get_entity_reference(rel_entities[0])
|
|
618
|
+
else:
|
|
619
|
+
data[rel_name] = [
|
|
620
|
+
self._get_entity_reference(e) for e in rel_entities
|
|
621
|
+
]
|
|
622
|
+
|
|
623
|
+
return data
|
|
624
|
+
|
|
625
|
+
def serialize(self) -> Dict[str, Any]:
|
|
626
|
+
"""Convert the entity to a portable serializable dictionary.
|
|
627
|
+
|
|
628
|
+
OneToMany relations are skipped (reconstructed from reverse ManyToOne).
|
|
629
|
+
For bilateral OneToOne relations, only the alphabetically-earlier entity
|
|
630
|
+
type serializes the reference, avoiding circular dependencies.
|
|
631
|
+
|
|
632
|
+
Returns:
|
|
633
|
+
Dict containing the entity's serializable data
|
|
634
|
+
"""
|
|
635
|
+
data = self._serialize_base()
|
|
636
|
+
|
|
637
|
+
from ic_python_db.properties import ManyToMany, OneToMany, OneToOne
|
|
596
638
|
|
|
597
639
|
for rel_name, rel_entities in self._relations.items():
|
|
598
640
|
if rel_entities:
|
|
599
|
-
# Check if this is a *ToMany relation that should always be a list
|
|
600
641
|
rel_prop = getattr(self.__class__, rel_name, None)
|
|
601
|
-
|
|
642
|
+
|
|
643
|
+
# Skip OneToMany — always reconstructed from reverse ManyToOne
|
|
644
|
+
if isinstance(rel_prop, OneToMany):
|
|
645
|
+
continue
|
|
646
|
+
# For OneToOne bilateral, only serialize on one deterministic side:
|
|
647
|
+
# the entity whose type name is alphabetically <= the target type.
|
|
648
|
+
if isinstance(rel_prop, OneToOne):
|
|
649
|
+
target_type = rel_entities[0]._type if rel_entities else None
|
|
650
|
+
if target_type and self._type > target_type:
|
|
651
|
+
continue
|
|
602
652
|
|
|
603
653
|
is_to_many = isinstance(rel_prop, (OneToMany, ManyToMany))
|
|
604
654
|
|
|
605
655
|
if len(rel_entities) == 1 and not is_to_many:
|
|
606
|
-
|
|
607
|
-
data[rel_name] = get_entity_reference(rel_entities[0])
|
|
656
|
+
data[rel_name] = self._get_entity_reference(rel_entities[0])
|
|
608
657
|
else:
|
|
609
|
-
|
|
610
|
-
|
|
658
|
+
data[rel_name] = [
|
|
659
|
+
self._get_entity_reference(e) for e in rel_entities
|
|
660
|
+
]
|
|
611
661
|
|
|
612
662
|
return data
|
|
613
663
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ic_python_db
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.6
|
|
4
4
|
Summary: A lightweight key-value database written in Python, intended for use on the Internet Computer (IC)
|
|
5
5
|
Home-page: https://github.com/smart-social-contracts/ic-python-db
|
|
6
6
|
Author: Smart Social Contracts
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ic_python_db"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.6"
|
|
8
8
|
description = "A lightweight key-value database written in Python, intended for use on the Internet Computer (IC)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="ic_python_db",
|
|
8
|
-
version="0.7.
|
|
8
|
+
version="0.7.6",
|
|
9
9
|
author="Smart Social Contracts",
|
|
10
10
|
author_email="smartsocialcontracts@gmail.com",
|
|
11
11
|
description="A lightweight key-value database with entity relationships and audit logging",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|