mirror of
https://github.com/django-polymorphic/django-polymorphic.git
synced 2026-02-14 02:30:23 +03:00
Add polymorphic guard to solve deletion problems.
Includes docs and robust tests.
This commit is contained in:
parent
c3136da180
commit
39a27e8c7c
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -61,6 +61,9 @@ local_settings.py
|
|||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Test migrations (generated dynamically by tests)
|
||||
src/polymorphic/tests/test_migrations/migrations/0*.py
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ API Documentation
|
|||
polymorphic.formsets
|
||||
polymorphic.managers
|
||||
polymorphic.models
|
||||
polymorphic.deletion
|
||||
polymorphic.query
|
||||
polymorphic.showfields
|
||||
polymorphic.templatetags
|
||||
|
|
|
|||
12
docs/api/polymorphic.deletion.rst
Normal file
12
docs/api/polymorphic.deletion.rst
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
polymorphic.models
|
||||
==================
|
||||
|
||||
.. automodule:: polymorphic.deletion
|
||||
|
||||
.. autoclass:: polymorphic.deletion.PolymorphicGuard
|
||||
:members: __call__
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: polymorphic.deletion.PolymorphicGuardSerializer
|
||||
:members:
|
||||
:show-inheritance:
|
||||
51
docs/deletion.rst
Normal file
51
docs/deletion.rst
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
Deletion
|
||||
========
|
||||
|
||||
.. versionadded:: 4.5.0
|
||||
|
||||
There is nothing special about deleting polymorphic models. The same rules apply as to
|
||||
:ref:`the deletion of normal Django models <topics-db-queries-delete>` that have parent/child
|
||||
relationships up and down a model inheritance hierarchy. Django must walk the model inheritance and
|
||||
relationship graph and collect all of the affected objects so that it can correctly order deletion
|
||||
SQL statements to respect database constraints and issue signals.
|
||||
|
||||
The polymorphic deletion logic is the same as the normal Django deletion logic because Django
|
||||
already walks the model inheritance hierarchy. :class:`~polymorphic.query.PolymorphicQuerySet` and
|
||||
:class:`~polymorphic.managers.PolymorphicManager` disrupt this process by confusing Django's graph
|
||||
walker by returning concrete subclass instances instead of base class instances when it attempts to
|
||||
walk reverse relationships to polymorphic models. To prevent this confusion,
|
||||
:pypi:`django-polymorphic` wraps the :attr:`~django.db.models.ForeignKey.on_delete` handlers of
|
||||
reverse relations to polymorphic models with :class:`~polymorphic.deletion.PolymorphicGuard`
|
||||
which disables polymorphic behavior on the related querysets during collection.
|
||||
|
||||
**You may define your polymorphic models as you normally would using the standard Django**
|
||||
:attr:`~django.db.models.ForeignKey.on_delete` **actions**.
|
||||
:class:`~polymorphic.models.PolymorphicModel` will automatically wrap the actions for you. actions
|
||||
wrapped with :class:`~polymorphic.deletion.PolymorphicGuard` serialize in migrations as the
|
||||
underlying wrapped action. This ensures migrations generated by versions of
|
||||
:pypi:`django-polymorphic` after 4.5.0 should be the same as with prior versions. The guard is also
|
||||
unnecessary during migrations because Django generates basic managers instead of using the default
|
||||
polymorphic managers.
|
||||
|
||||
It is a design goal of :pypi:`django-polymorphic` that deletion should just work without any special
|
||||
treatment. However if you encounter attribute errors or database integrity errors during deletion
|
||||
you may manually wrap the :attr:`~django.db.models.ForeignKey.on_delete` action of reverse relations
|
||||
to polymorphic models with :class:`~polymorphic.deletion.PolymorphicGuard` to disable polymorphic
|
||||
behavior during deletion collection. If you encounter an issue like this
|
||||
`please report it to us <https://github.com/jazzband/django-polymorphic/issues>`_. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from polymorphic.models import PolymorphicModel
|
||||
from polymorphic.deletion import PolymorphicGuard
|
||||
from django.db import models
|
||||
|
||||
class MyModel(models.Model):
|
||||
# ...
|
||||
|
||||
class RelatedModel(PolymorphicModel):
|
||||
my_model = models.ForeignKey(
|
||||
MyModel,
|
||||
on_delete=PolymorphicGuard(models.CASCADE),
|
||||
)
|
||||
|
||||
|
|
@ -119,6 +119,7 @@ Advanced topics
|
|||
formsets
|
||||
migrating
|
||||
managers
|
||||
deletion
|
||||
advanced
|
||||
changelog
|
||||
api/index
|
||||
|
|
|
|||
1
justfile
1
justfile
|
|
@ -97,6 +97,7 @@ build: build-docs-html
|
|||
# regenerate test migrations using the lowest version of Django
|
||||
make-test-migrations:
|
||||
- rm src/polymorphic/tests/migrations/00*.py
|
||||
- rm src/polymorphic/tests/deletion/migrations/00*.py
|
||||
uv run --isolated --resolution lowest-direct --script ./manage.py makemigrations
|
||||
|
||||
# open the html documentation
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ show_missing = true
|
|||
dev = [
|
||||
"coverage>=7.6.1",
|
||||
"dj-database-url>=2.2.0",
|
||||
"django-test-migrations>=1.5.0",
|
||||
"ipdb>=0.13.13",
|
||||
"ipython>=8.18.1",
|
||||
"mypy>=1.14.1",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import warnings
|
|||
from django.db import models
|
||||
from django.db.models.base import ModelBase
|
||||
|
||||
from .deletion import PolymorphicGuard
|
||||
from .managers import PolymorphicManager
|
||||
from .query import PolymorphicQuerySet
|
||||
|
||||
|
|
@ -75,6 +76,14 @@ class PolymorphicModelBase(ModelBase):
|
|||
if new_class._meta.pk:
|
||||
new_class.polymorphic_primary_key_name = new_class._meta.pk.name
|
||||
|
||||
# wrap on_delete handlers of reverse relations back to this model with the
|
||||
# polymorphic deletion guard
|
||||
for fk in new_class._meta.fields:
|
||||
if isinstance(fk, (models.ForeignKey, models.OneToOneField)) and not isinstance(
|
||||
fk.remote_field.on_delete, PolymorphicGuard
|
||||
):
|
||||
fk.remote_field.on_delete = PolymorphicGuard(fk.remote_field.on_delete)
|
||||
|
||||
return new_class
|
||||
|
||||
@classmethod
|
||||
|
|
|
|||
64
src/polymorphic/deletion.py
Normal file
64
src/polymorphic/deletion.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
"""
|
||||
Classes and utilities for handling deletions in polymorphic models.
|
||||
"""
|
||||
|
||||
from django.db.migrations.serializer import BaseSerializer, serializer_factory
|
||||
from django.db.migrations.writer import MigrationWriter
|
||||
|
||||
from .query import PolymorphicQuerySet
|
||||
|
||||
|
||||
class PolymorphicGuard:
|
||||
"""
|
||||
Wrap an :attr:`django.db.models.ForeignKey.on_delete` callable
|
||||
(CASCADE/PROTECT/SET_NULL/SET(...)/custom), but serialize as the underlying
|
||||
callable.
|
||||
|
||||
:param action: The :attr:`django.db.models.ForeignKey.on_delete` callable to wrap.
|
||||
"""
|
||||
|
||||
def __init__(self, action):
|
||||
if not callable(action):
|
||||
raise TypeError("action must be callable")
|
||||
self.action = action
|
||||
|
||||
def __call__(self, collector, field, sub_objs, using):
|
||||
"""
|
||||
This guard wraps an on_delete action to ensure that any polymorphic queryset
|
||||
passed to it is converted to a non-polymorphic queryset before proceeding.
|
||||
This prevents issues with cascading deletes on polymorphic models.
|
||||
|
||||
This guard should be automatically applied to reverse relations such that
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyModel(PolymorphicModel):
|
||||
related = models.ForeignKey(
|
||||
OtherModel,
|
||||
on_delete=models.CASCADE # <- equal to PolymorphicGuard(models.CASCADE)
|
||||
)
|
||||
|
||||
"""
|
||||
if isinstance(sub_objs, PolymorphicQuerySet) and not sub_objs.polymorphic_disabled:
|
||||
sub_objs = sub_objs.non_polymorphic()
|
||||
return self.action(collector, field, sub_objs, using)
|
||||
|
||||
|
||||
class PolymorphicGuardSerializer(BaseSerializer):
|
||||
"""
|
||||
A serializer for PolymorphicGuard that serializes the underlying action.
|
||||
|
||||
There is no need to serialize the PolymorphicGuard itself, as it is just a wrapper
|
||||
that ensures that polymorphic querysets are converted to non-polymorphic but no
|
||||
polymorphic managers are present in migrations. This also ensures that new
|
||||
migrations will not be generated.
|
||||
"""
|
||||
|
||||
def serialize(self):
|
||||
"""
|
||||
Serialize the underlying action of the PolymorphicGuard.
|
||||
"""
|
||||
return serializer_factory(self.value.action).serialize()
|
||||
|
||||
|
||||
MigrationWriter.register_serializer(PolymorphicGuard, PolymorphicGuardSerializer)
|
||||
|
|
@ -546,3 +546,12 @@ class PolymorphicQuerySet(QuerySet):
|
|||
return olist
|
||||
clist = PolymorphicQuerySet._p_list_class(olist)
|
||||
return clist
|
||||
|
||||
def delete(self):
|
||||
"""
|
||||
Deletion will be done non-polymorphically because Django's multi-table deletion
|
||||
mechanism is already walking the class hierarchy and producing a correct
|
||||
deletion graph. Introducing polymorphic querysets into the deletion process
|
||||
disrupts the model hierarchy/relationship traversal.
|
||||
"""
|
||||
return QuerySet.delete(self.non_polymorphic())
|
||||
|
|
|
|||
0
src/polymorphic/tests/deletion/__init__.py
Normal file
0
src/polymorphic/tests/deletion/__init__.py
Normal file
871
src/polymorphic/tests/deletion/migrations/0001_initial.py
Normal file
871
src/polymorphic/tests/deletion/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,871 @@
|
|||
# Generated by Django 4.2 on 2025-12-20 11:18
|
||||
|
||||
from decimal import Decimal
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import polymorphic.tests.deletion.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='A_160',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='A_160Plain',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='A_274',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='A_540',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
|
||||
('self_referential', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='deletion.a_540')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Animal',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Answer',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='B_160',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('a', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='deletion.a_160')),
|
||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='B_160Plain',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('a', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='deletion.a_160plain')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Base',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Beneficiary',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('firstname', models.CharField(max_length=100)),
|
||||
('lastname', models.CharField(max_length=100)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomModel',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Farm',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Normal2',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Normal3',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Normal4',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Normal5',
|
||||
fields=[
|
||||
('n_pk', models.AutoField(primary_key=True, serialize=False)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Order',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=200, verbose_name='Title')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('title',),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Payment',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('amount', models.DecimalField(blank=True, decimal_places=2, default=Decimal('0'), max_digits=10)),
|
||||
('index', models.PositiveIntegerField(default=0)),
|
||||
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='deletion.order')),
|
||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('index',),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PlainA',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Poll',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Poly1',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Poly2',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('normal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='polies', to='deletion.normal2')),
|
||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Poly4',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('normals', models.ManyToManyField(blank=True, related_name='polies', to='deletion.normal4')),
|
||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Poly4_1',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PolyDevice',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=64)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PolyInterface',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=64)),
|
||||
('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='deletion.polydevice')),
|
||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Standalone',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='A1',
|
||||
fields=[
|
||||
('poly1_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.poly1')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly1',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='A2',
|
||||
fields=[
|
||||
('poly2_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.poly2')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly2',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='A4',
|
||||
fields=[
|
||||
('poly4_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.poly4')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly4',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='A4_1',
|
||||
fields=[
|
||||
('poly4_1_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.poly4_1')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly4_1',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='B1',
|
||||
fields=[
|
||||
('poly1_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.poly1')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly1',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='B1_160',
|
||||
fields=[
|
||||
('b_160_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.b_160')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.b_160',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='B1_160Plain',
|
||||
fields=[
|
||||
('b_160plain_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.b_160plain')),
|
||||
],
|
||||
bases=('deletion.b_160plain',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='B2',
|
||||
fields=[
|
||||
('poly2_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.poly2')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly2',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='B2_160',
|
||||
fields=[
|
||||
('b_160_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.b_160')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.b_160',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='B2_160Plain',
|
||||
fields=[
|
||||
('b_160plain_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.b_160plain')),
|
||||
],
|
||||
bases=('deletion.b_160plain',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='B4',
|
||||
fields=[
|
||||
('poly4_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.poly4')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly4',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='B4_1',
|
||||
fields=[
|
||||
('poly4_1_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.poly4_1')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly4_1',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='B_274',
|
||||
fields=[
|
||||
('a_274_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.a_274')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.a_274',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='B_540',
|
||||
fields=[
|
||||
('a_540_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.a_540')),
|
||||
('name', models.CharField(max_length=256)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.a_540',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='C1',
|
||||
fields=[
|
||||
('poly1_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.poly1')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly1',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='C2',
|
||||
fields=[
|
||||
('poly2_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.poly2')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly2',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='C4',
|
||||
fields=[
|
||||
('poly4_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.poly4')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly4',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='C4_1',
|
||||
fields=[
|
||||
('poly4_1_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.poly4_1')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly4_1',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Cat',
|
||||
fields=[
|
||||
('animal_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.animal')),
|
||||
('cat_param', models.CharField(max_length=100)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.animal',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Child',
|
||||
fields=[
|
||||
('base_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.base')),
|
||||
],
|
||||
bases=('deletion.base',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CreditCardPayment',
|
||||
fields=[
|
||||
('payment_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.payment')),
|
||||
('card_type', models.CharField(max_length=32)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.payment',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='D_274',
|
||||
fields=[
|
||||
('a_274_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.a_274')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.a_274',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DatasetFolder',
|
||||
fields=[
|
||||
('custommodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.custommodel')),
|
||||
],
|
||||
bases=('deletion.custommodel',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Dog',
|
||||
fields=[
|
||||
('animal_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.animal')),
|
||||
('dog_param', models.CharField(max_length=100)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.animal',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='OriginalFile',
|
||||
fields=[
|
||||
('custommodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.custommodel')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('content_type', models.CharField(max_length=100)),
|
||||
('size', models.PositiveIntegerField()),
|
||||
('dataset_folder', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='deletion.datasetfolder')),
|
||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.custommodel', models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PlainB1',
|
||||
fields=[
|
||||
('plaina_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.plaina')),
|
||||
('standalones', models.ManyToManyField(to='deletion.standalone')),
|
||||
],
|
||||
bases=('deletion.plaina',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Poly3',
|
||||
fields=[
|
||||
('normal3_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.normal3')),
|
||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.normal3', models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Poly5',
|
||||
fields=[
|
||||
('normal5_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, to='deletion.normal5')),
|
||||
('p_pk', models.AutoField(primary_key=True, serialize=False)),
|
||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.normal5', models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PolyEthernetInterface',
|
||||
fields=[
|
||||
('polyinterface_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.polyinterface')),
|
||||
('ethernety_stuff', models.CharField(max_length=64)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.polyinterface',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PolyFCInterface',
|
||||
fields=[
|
||||
('polyinterface_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.polyinterface')),
|
||||
('fc_stuff', models.CharField(max_length=64)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.polyinterface',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PolyWirelessInterface',
|
||||
fields=[
|
||||
('polyinterface_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.polyinterface')),
|
||||
('wirelessy_stuff', models.CharField(max_length=64)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.polyinterface',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TextAnswer',
|
||||
fields=[
|
||||
('answer_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.answer')),
|
||||
('answer', models.CharField(blank=True, default='', max_length=500)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.answer',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='YesNoAnswer',
|
||||
fields=[
|
||||
('answer_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.answer')),
|
||||
('answer', models.BooleanField(default=False, verbose_name='answer')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.answer',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Question',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('poll', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='deletion.poll')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='plaina',
|
||||
name='standalone_parent',
|
||||
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='plainas', to='deletion.standalone'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Normal4_1',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('polies', models.ManyToManyField(blank=True, related_name='normals', to='deletion.poly4_1')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Normal1',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('poly', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='deletion.poly1')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='answer',
|
||||
name='question',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='deletion.question'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='animal',
|
||||
name='farm',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='animals', to='deletion.farm'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='animal',
|
||||
name='polymorphic_ctype',
|
||||
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='A3',
|
||||
fields=[
|
||||
('poly3_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.poly3')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly3',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='A5',
|
||||
fields=[
|
||||
('poly5_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, to='deletion.poly5')),
|
||||
('a_pk', models.AutoField(primary_key=True, serialize=False)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly5',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='B3',
|
||||
fields=[
|
||||
('poly3_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.poly3')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly3',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='B5',
|
||||
fields=[
|
||||
('poly5_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, to='deletion.poly5')),
|
||||
('b_pk', models.AutoField(primary_key=True, serialize=False)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.poly5',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='C_274',
|
||||
fields=[
|
||||
('b_274_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.b_274')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.b_274',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DatasetRelation',
|
||||
fields=[
|
||||
('originalfile_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.originalfile')),
|
||||
('file', models.FileField(max_length=500, upload_to=polymorphic.tests.deletion.models.project_directory_path)),
|
||||
('original_file_name', models.CharField(max_length=100)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.originalfile',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='E_274',
|
||||
fields=[
|
||||
('d_274_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.d_274')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.d_274',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='GrandChild',
|
||||
fields=[
|
||||
('child_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.child')),
|
||||
],
|
||||
bases=('deletion.child',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='OriginalImage',
|
||||
fields=[
|
||||
('originalfile_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.originalfile')),
|
||||
('original_file_name', models.CharField(max_length=100)),
|
||||
('file', models.FileField(max_length=500, upload_to=polymorphic.tests.deletion.models.project_directory_path)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.originalfile',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PolyFixedInterface',
|
||||
fields=[
|
||||
('polyethernetinterface_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.polyethernetinterface')),
|
||||
('fixed_stuff', models.CharField(max_length=64)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.polyethernetinterface',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PolyModularInterface',
|
||||
fields=[
|
||||
('polyethernetinterface_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.polyethernetinterface')),
|
||||
('modular_stuff', models.CharField(max_length=64)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.polyethernetinterface',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SepaPayment',
|
||||
fields=[
|
||||
('payment_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.payment')),
|
||||
('iban', models.CharField(max_length=34)),
|
||||
('bic', models.CharField(max_length=11)),
|
||||
('beneficiaries', models.ManyToManyField(blank=True, related_name='sepa', to='deletion.beneficiary')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.payment',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='RelatedToChild',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('child', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='relatives', to='deletion.child')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Project',
|
||||
fields=[
|
||||
('custommodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.custommodel')),
|
||||
('name', models.CharField(max_length=30)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
bases=('deletion.custommodel',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PlainB2',
|
||||
fields=[
|
||||
('plaina_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.plaina')),
|
||||
('standalone', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='plainb2s', to='deletion.standalone')),
|
||||
],
|
||||
bases=('deletion.plaina',),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='datasetfolder',
|
||||
name='prjct',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='deletion.project'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='C_160Plain',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('b', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='deletion.b1_160plain')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='C_160',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('b', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='deletion.b1_160')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='RelatedToGrandChild',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('grand_child', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='grand_relatives', to='deletion.grandchild')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PlainC1',
|
||||
fields=[
|
||||
('plainb1_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.plainb1')),
|
||||
('standalone', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='plainc1s', to='deletion.standalone')),
|
||||
],
|
||||
bases=('deletion.plainb1',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='OriginalDataset',
|
||||
fields=[
|
||||
('originalfile_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='deletion.originalfile')),
|
||||
('file', models.FileField(max_length=500, upload_to=polymorphic.tests.deletion.models.project_directory_path)),
|
||||
('original_file_name', models.CharField(blank=True, max_length=100, null=True)),
|
||||
('table_name', models.CharField(blank=True, max_length=100, null=True)),
|
||||
('rows_number', models.PositiveIntegerField()),
|
||||
('dataset_metadata', models.JSONField()),
|
||||
('dataset_relation', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='deletion.datasetrelation')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
'base_manager_name': 'objects',
|
||||
},
|
||||
bases=('deletion.originalfile',),
|
||||
),
|
||||
]
|
||||
491
src/polymorphic/tests/deletion/models.py
Normal file
491
src/polymorphic/tests/deletion/models.py
Normal file
|
|
@ -0,0 +1,491 @@
|
|||
from django.db import models
|
||||
from django.conf import settings
|
||||
from polymorphic.models import PolymorphicModel
|
||||
from polymorphic.deletion import PolymorphicGuard
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
def project_directory_path(instance, filename):
|
||||
# just to satisfy upload_to; keep it deterministic for tests
|
||||
return f"p{instance.dataset_folder.prjct_id}/{filename}"
|
||||
|
||||
|
||||
CASCADE = models.CASCADE
|
||||
|
||||
|
||||
class Standalone(models.Model):
|
||||
pass
|
||||
|
||||
|
||||
class PlainA(models.Model):
|
||||
standalone_parent = models.ForeignKey(
|
||||
Standalone, on_delete=CASCADE, null=True, default=None, related_name="plainas"
|
||||
)
|
||||
|
||||
|
||||
class PlainB1(PlainA):
|
||||
standalones = models.ManyToManyField(Standalone)
|
||||
|
||||
|
||||
class PlainB2(PlainA):
|
||||
standalone = models.ForeignKey(
|
||||
Standalone, on_delete=CASCADE, null=True, default=None, related_name="plainb2s"
|
||||
)
|
||||
|
||||
|
||||
class PlainC1(PlainB1):
|
||||
standalone = models.ForeignKey(
|
||||
Standalone, on_delete=CASCADE, null=True, default=None, related_name="plainc1s"
|
||||
)
|
||||
|
||||
|
||||
class RelatedToChild(models.Model):
|
||||
child = models.ForeignKey(
|
||||
"Child", on_delete=CASCADE, null=True, default=None, related_name="relatives"
|
||||
)
|
||||
|
||||
|
||||
class Base(models.Model):
|
||||
pass
|
||||
|
||||
|
||||
class Child(Base):
|
||||
pass
|
||||
|
||||
|
||||
class GrandChild(Child):
|
||||
pass
|
||||
|
||||
|
||||
class RelatedToGrandChild(models.Model):
|
||||
grand_child = models.ForeignKey(
|
||||
GrandChild, on_delete=CASCADE, null=True, default=None, related_name="grand_relatives"
|
||||
)
|
||||
|
||||
|
||||
###########################################################
|
||||
|
||||
"""
|
||||
Scenario 1
|
||||
|
||||
<-- cascade --
|
||||
Normal1 ----- FK ----> Poly1
|
||||
/ | \
|
||||
A1 B1 C1
|
||||
"""
|
||||
|
||||
|
||||
class Normal1(models.Model):
|
||||
poly = models.ForeignKey("Poly1", on_delete=models.CASCADE) # <-- this is fine
|
||||
|
||||
|
||||
class Poly1(PolymorphicModel):
|
||||
pass
|
||||
|
||||
|
||||
class A1(Poly1):
|
||||
pass
|
||||
|
||||
|
||||
class B1(Poly1):
|
||||
pass
|
||||
|
||||
|
||||
class C1(Poly1):
|
||||
pass
|
||||
|
||||
|
||||
"""
|
||||
Scenario 2
|
||||
|
||||
-- cascade -->
|
||||
Normal2 <----- FK ---- Poly2
|
||||
/ | \
|
||||
A2 B2 C2
|
||||
"""
|
||||
|
||||
|
||||
class Normal2(models.Model):
|
||||
pass
|
||||
|
||||
|
||||
class Poly2(PolymorphicModel):
|
||||
normal = models.ForeignKey(Normal2, on_delete=CASCADE, related_name="polies")
|
||||
|
||||
|
||||
class A2(Poly2):
|
||||
pass
|
||||
|
||||
|
||||
class B2(Poly2):
|
||||
pass
|
||||
|
||||
|
||||
class C2(Poly2):
|
||||
pass
|
||||
|
||||
|
||||
"""
|
||||
Scenario 3
|
||||
|
||||
Normal3
|
||||
|
|
||||
Poly3
|
||||
| \
|
||||
A3 B3
|
||||
"""
|
||||
|
||||
|
||||
class Normal3(models.Model):
|
||||
pass
|
||||
|
||||
|
||||
class Poly3(PolymorphicModel, Normal3):
|
||||
pass
|
||||
|
||||
|
||||
class A3(Poly3):
|
||||
pass
|
||||
|
||||
|
||||
class B3(Poly3):
|
||||
pass
|
||||
|
||||
|
||||
"""
|
||||
Scenario 4
|
||||
|
||||
<--- cascade --->
|
||||
Normal4 <----- M2M -----> Poly4
|
||||
/ | \
|
||||
A4 B4 C4
|
||||
"""
|
||||
|
||||
|
||||
class Normal4(models.Model):
|
||||
pass
|
||||
|
||||
|
||||
class Poly4(PolymorphicModel):
|
||||
normals = models.ManyToManyField(Normal4, related_name="polies", blank=True)
|
||||
|
||||
|
||||
class A4(Poly4):
|
||||
pass
|
||||
|
||||
|
||||
class B4(Poly4):
|
||||
pass
|
||||
|
||||
|
||||
class C4(Poly4):
|
||||
pass
|
||||
|
||||
|
||||
class Normal4_1(models.Model):
|
||||
polies = models.ManyToManyField("Poly4_1", related_name="normals", blank=True)
|
||||
|
||||
|
||||
class Poly4_1(PolymorphicModel):
|
||||
pass
|
||||
|
||||
|
||||
class A4_1(Poly4_1):
|
||||
pass
|
||||
|
||||
|
||||
class B4_1(Poly4_1):
|
||||
pass
|
||||
|
||||
|
||||
class C4_1(Poly4_1):
|
||||
pass
|
||||
|
||||
|
||||
"""
|
||||
Scenario 5 - scenario3 with custom/different PKs
|
||||
|
||||
Normal3
|
||||
|
|
||||
Poly3
|
||||
| \
|
||||
A3 B3
|
||||
"""
|
||||
|
||||
|
||||
class Normal5(models.Model):
|
||||
n_pk = models.AutoField(primary_key=True)
|
||||
|
||||
|
||||
class Poly5(PolymorphicModel, Normal5):
|
||||
p_pk = models.AutoField(primary_key=True)
|
||||
|
||||
|
||||
class A5(Poly5):
|
||||
a_pk = models.AutoField(primary_key=True)
|
||||
|
||||
|
||||
class B5(Poly5):
|
||||
b_pk = models.AutoField(primary_key=True)
|
||||
|
||||
|
||||
########################################################################################
|
||||
# There were 15 years of deletion bug reports - many were duplicates but many also
|
||||
# included example models. We copy all of these provided tests in here not being too
|
||||
# concerned about redundancy - we just want to make sure we don't regress on any of
|
||||
# them. Each block is tagged with the root issue. In some cases we mirror the setup
|
||||
# with a plain django model parallel - mostly for debug/comparison purposes
|
||||
|
||||
###########################################################
|
||||
# https://github.com/jazzband/django-polymorphic/issues/160
|
||||
|
||||
|
||||
class A_160(models.Model):
|
||||
pass
|
||||
|
||||
|
||||
class B_160(PolymorphicModel):
|
||||
a = models.ForeignKey(A_160, on_delete=CASCADE)
|
||||
|
||||
|
||||
class B1_160(B_160):
|
||||
pass
|
||||
|
||||
|
||||
class B2_160(B_160):
|
||||
pass
|
||||
|
||||
|
||||
class C_160(models.Model):
|
||||
b = models.ForeignKey(B1_160, on_delete=CASCADE)
|
||||
|
||||
|
||||
# Plain
|
||||
class A_160Plain(models.Model):
|
||||
pass
|
||||
|
||||
|
||||
class B_160Plain(models.Model):
|
||||
a = models.ForeignKey(A_160Plain, on_delete=CASCADE)
|
||||
|
||||
|
||||
class B1_160Plain(B_160Plain):
|
||||
pass
|
||||
|
||||
|
||||
class B2_160Plain(B_160Plain):
|
||||
pass
|
||||
|
||||
|
||||
class C_160Plain(models.Model):
|
||||
# test that guard misapplication is fine
|
||||
b = models.ForeignKey(B1_160Plain, on_delete=PolymorphicGuard(CASCADE))
|
||||
|
||||
|
||||
###########################################################
|
||||
###########################################################
|
||||
# https://github.com/jazzband/django-polymorphic/issues/229
|
||||
|
||||
|
||||
class Farm(models.Model):
|
||||
pass
|
||||
|
||||
|
||||
class Animal(PolymorphicModel):
|
||||
farm = models.ForeignKey(
|
||||
"Farm", on_delete=PolymorphicGuard(models.CASCADE), related_name="animals"
|
||||
)
|
||||
name = models.CharField(max_length=100)
|
||||
|
||||
|
||||
class Dog(Animal):
|
||||
dog_param = models.CharField(max_length=100)
|
||||
|
||||
|
||||
class Cat(Animal):
|
||||
cat_param = models.CharField(max_length=100)
|
||||
|
||||
|
||||
###########################################################
|
||||
# https://github.com/jazzband/django-polymorphic/issues/274
|
||||
|
||||
|
||||
class A_274(PolymorphicModel):
|
||||
pass
|
||||
|
||||
|
||||
class B_274(A_274):
|
||||
pass
|
||||
|
||||
|
||||
class D_274(A_274):
|
||||
pass
|
||||
|
||||
|
||||
class E_274(D_274):
|
||||
pass
|
||||
|
||||
|
||||
class C_274(B_274):
|
||||
pass
|
||||
|
||||
|
||||
###########################################################
|
||||
###########################################################
|
||||
# https://github.com/jazzband/django-polymorphic/issues/357
|
||||
|
||||
|
||||
class Order(models.Model):
|
||||
title = models.CharField("Title", max_length=200)
|
||||
|
||||
class Meta:
|
||||
ordering = ("title",)
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class Payment(PolymorphicModel):
|
||||
order = models.ForeignKey(Order, on_delete=CASCADE)
|
||||
amount = models.DecimalField(default=Decimal(0.0), blank=True, max_digits=10, decimal_places=2)
|
||||
index = models.PositiveIntegerField(default=0, blank=False)
|
||||
|
||||
class Meta:
|
||||
ordering = ("index",)
|
||||
|
||||
|
||||
class CreditCardPayment(Payment):
|
||||
card_type = models.CharField(max_length=32)
|
||||
|
||||
|
||||
class Beneficiary(models.Model):
|
||||
firstname = models.CharField(max_length=100)
|
||||
lastname = models.CharField(max_length=100)
|
||||
|
||||
|
||||
class SepaPayment(Payment):
|
||||
iban = models.CharField(max_length=34)
|
||||
bic = models.CharField(max_length=11)
|
||||
beneficiaries = models.ManyToManyField(Beneficiary, "sepa", blank=True)
|
||||
|
||||
|
||||
###########################################################
|
||||
###########################################################
|
||||
# https://github.com/jazzband/django-polymorphic/issues/481
|
||||
|
||||
# no example given - how to?
|
||||
|
||||
|
||||
###########################################################
|
||||
###########################################################
|
||||
# https://github.com/jazzband/django-polymorphic/issues/540
|
||||
|
||||
|
||||
class A_540(PolymorphicModel):
|
||||
self_referential = models.ForeignKey("self", null=True, blank=True, on_delete=CASCADE)
|
||||
|
||||
|
||||
class B_540(A_540):
|
||||
name = models.CharField(max_length=256)
|
||||
|
||||
|
||||
###########################################################
|
||||
###########################################################
|
||||
# https://github.com/jazzband/django-polymorphic/issues/547
|
||||
|
||||
|
||||
class CustomModel(models.Model):
|
||||
pass
|
||||
|
||||
|
||||
class Project(CustomModel):
|
||||
name = models.CharField(max_length=30)
|
||||
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=CASCADE)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
|
||||
class DatasetFolder(CustomModel):
|
||||
prjct = models.ForeignKey(Project, on_delete=CASCADE)
|
||||
|
||||
|
||||
class OriginalFile(PolymorphicModel, CustomModel):
|
||||
dataset_folder = models.ForeignKey(DatasetFolder, on_delete=CASCADE)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
content_type = models.CharField(max_length=100)
|
||||
size = models.PositiveIntegerField()
|
||||
|
||||
|
||||
class DatasetRelation(OriginalFile):
|
||||
file = models.FileField(max_length=500, upload_to=project_directory_path)
|
||||
original_file_name = models.CharField(max_length=100)
|
||||
|
||||
|
||||
class OriginalDataset(OriginalFile):
|
||||
dataset_relation = models.ForeignKey(
|
||||
DatasetRelation, on_delete=models.SET_NULL, blank=True, null=True
|
||||
)
|
||||
file = models.FileField(max_length=500, upload_to=project_directory_path)
|
||||
original_file_name = models.CharField(max_length=100, null=True, blank=True)
|
||||
table_name = models.CharField(max_length=100, null=True, blank=True)
|
||||
rows_number = models.PositiveIntegerField()
|
||||
dataset_metadata = models.JSONField()
|
||||
|
||||
|
||||
class OriginalImage(OriginalFile):
|
||||
original_file_name = models.CharField(max_length=100)
|
||||
file = models.FileField(max_length=500, upload_to=project_directory_path)
|
||||
|
||||
|
||||
###########################################################
|
||||
###########################################################
|
||||
# https://github.com/jazzband/django-polymorphic/issues/608
|
||||
|
||||
|
||||
class PolyDevice(models.Model):
|
||||
name = models.CharField(max_length=64)
|
||||
|
||||
|
||||
class PolyInterface(PolymorphicModel):
|
||||
name = models.CharField(max_length=64)
|
||||
device = models.ForeignKey(to=PolyDevice, on_delete=CASCADE)
|
||||
|
||||
|
||||
class PolyEthernetInterface(PolyInterface):
|
||||
ethernety_stuff = models.CharField(max_length=64)
|
||||
|
||||
|
||||
class PolyModularInterface(PolyEthernetInterface):
|
||||
modular_stuff = models.CharField(max_length=64)
|
||||
|
||||
|
||||
class PolyFixedInterface(PolyEthernetInterface):
|
||||
fixed_stuff = models.CharField(max_length=64)
|
||||
|
||||
|
||||
class PolyWirelessInterface(PolyInterface):
|
||||
wirelessy_stuff = models.CharField(max_length=64)
|
||||
|
||||
|
||||
class PolyFCInterface(PolyInterface):
|
||||
fc_stuff = models.CharField(max_length=64)
|
||||
|
||||
|
||||
class Poll(models.Model):
|
||||
pass
|
||||
|
||||
|
||||
class Question(models.Model):
|
||||
poll = models.ForeignKey(to=Poll, on_delete=CASCADE)
|
||||
|
||||
|
||||
class Answer(PolymorphicModel):
|
||||
question = models.ForeignKey(to=Question, on_delete=CASCADE)
|
||||
|
||||
|
||||
class TextAnswer(Answer):
|
||||
answer = models.CharField(default="", blank=True, max_length=500)
|
||||
|
||||
|
||||
class YesNoAnswer(Answer):
|
||||
answer = models.BooleanField("answer", default=False)
|
||||
741
src/polymorphic/tests/deletion/test_deletion.py
Normal file
741
src/polymorphic/tests/deletion/test_deletion.py
Normal file
|
|
@ -0,0 +1,741 @@
|
|||
from django.test import TestCase
|
||||
import shutil
|
||||
import tempfile
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.files.base import ContentFile
|
||||
from django.test.utils import CaptureQueriesContext
|
||||
from django.test import override_settings
|
||||
from django.db import connection
|
||||
|
||||
|
||||
@override_settings()
|
||||
class TestDeletion(TestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self._media_root = tempfile.mkdtemp(prefix="test-media-")
|
||||
self._media_override = override_settings(MEDIA_ROOT=self._media_root)
|
||||
self._media_override.enable()
|
||||
|
||||
def tearDown(self):
|
||||
self._media_override.disable()
|
||||
shutil.rmtree(self._media_root, ignore_errors=True)
|
||||
super().tearDown()
|
||||
|
||||
def test_deletion_bug_160(self):
|
||||
"""https://github.com/jazzband/django-polymorphic/issues/160"""
|
||||
from .models import A_160, B_160, B1_160, B2_160, C_160
|
||||
from .models import A_160Plain, B_160Plain, C_160Plain, B1_160Plain, B2_160Plain
|
||||
|
||||
a = A_160Plain.objects.create()
|
||||
a2 = A_160Plain.objects.create()
|
||||
b1 = B1_160Plain.objects.create(a=a)
|
||||
b2 = B2_160Plain.objects.create(a=a)
|
||||
b2_2 = B2_160Plain.objects.create(a=a2)
|
||||
c = C_160Plain.objects.create(b=b1)
|
||||
a.delete()
|
||||
assert [a2] == list(A_160Plain.objects.all())
|
||||
assert B_160Plain.objects.count() == 1
|
||||
assert B1_160Plain.objects.count() == 0
|
||||
assert [b2_2] == list(B2_160Plain.objects.all())
|
||||
assert C_160Plain.objects.count() == 0
|
||||
|
||||
a = A_160.objects.create()
|
||||
a2 = A_160.objects.create()
|
||||
b1 = B1_160.objects.create(a=a)
|
||||
b2 = B2_160.objects.create(a=a)
|
||||
b2_2 = B2_160.objects.create(a=a2)
|
||||
c = C_160.objects.create(b=b1)
|
||||
a.delete()
|
||||
assert [a2] == list(A_160.objects.all())
|
||||
assert B_160.objects.count() == 1
|
||||
assert B1_160.objects.count() == 0
|
||||
assert [b2_2] == list(B2_160.objects.all())
|
||||
assert C_160.objects.count() == 0
|
||||
|
||||
def test_deletion_bug_229(self):
|
||||
"""
|
||||
https://github.com/jazzband/django-polymorphic/issues/229
|
||||
"""
|
||||
from .models import Farm, Animal, Dog, Cat
|
||||
|
||||
farm = Farm.objects.create()
|
||||
Dog.objects.create(farm=farm, name="Rex", dog_param="kibble")
|
||||
Cat.objects.create(farm=farm, name="Misty", cat_param="whiskers")
|
||||
farm.delete()
|
||||
assert Animal.objects.count() == 0
|
||||
assert Dog.objects.count() == 0
|
||||
assert Cat.objects.count() == 0
|
||||
assert Farm.objects.count() == 0
|
||||
|
||||
farm = Farm.objects.create()
|
||||
Dog.objects.create(farm=farm, name="Rex", dog_param="kibble")
|
||||
Cat.objects.create(farm=farm, name="Misty", cat_param="whiskers")
|
||||
farm2 = Farm.objects.create()
|
||||
hugo = Cat.objects.create(farm=farm2, name="Hugo", cat_param="10")
|
||||
marlo = Cat.objects.create(farm=farm2, name="Marlo", cat_param="14")
|
||||
assert Animal.objects.count() == 4
|
||||
farm.delete()
|
||||
assert Animal.objects.count() == 2
|
||||
assert hugo in Cat.objects.all()
|
||||
assert marlo in Cat.objects.all()
|
||||
assert hugo in Animal.objects.all()
|
||||
assert marlo in Animal.objects.all()
|
||||
assert hugo in farm2.animals.all()
|
||||
assert marlo in farm2.animals.all()
|
||||
|
||||
Animal.objects.all().delete()
|
||||
assert Animal.objects.count() == 0
|
||||
assert Dog.objects.count() == 0
|
||||
assert Cat.objects.count() == 0
|
||||
assert Farm.objects.count() == 1
|
||||
assert farm2 in Farm.objects.all()
|
||||
assert farm2.animals.count() == 0
|
||||
|
||||
def test_deletion_bug_274(self):
|
||||
"""
|
||||
https://github.com/jazzband/django-polymorphic/issues/274
|
||||
"""
|
||||
from .models import A_274, B_274, C_274, D_274, E_274
|
||||
|
||||
B_274.objects.create()
|
||||
D_274.objects.create()
|
||||
E_274.objects.create()
|
||||
A_274.objects.all().delete()
|
||||
|
||||
assert A_274.objects.count() == 0
|
||||
assert B_274.objects.count() == 0
|
||||
assert D_274.objects.count() == 0
|
||||
assert E_274.objects.count() == 0
|
||||
assert C_274.objects.count() == 0
|
||||
|
||||
def test_deletion_bug_357(self):
|
||||
"""
|
||||
https://github.com/jazzband/django-polymorphic/issues/357
|
||||
"""
|
||||
from .models import Order, Payment, CreditCardPayment, SepaPayment, Beneficiary
|
||||
|
||||
order1 = Order.objects.create(title="Order 1")
|
||||
payment1 = SepaPayment.objects.create(
|
||||
order=order1,
|
||||
amount=100.00,
|
||||
iban="DE89370400440532013000",
|
||||
bic="COBADEFFXXX",
|
||||
)
|
||||
CreditCardPayment.objects.create(
|
||||
order=order1,
|
||||
amount=100.00,
|
||||
card_type="VISA",
|
||||
)
|
||||
bk = Beneficiary.objects.create(firstname="Brian", lastname="Kohan")
|
||||
ea = Beneficiary.objects.create(firstname="Edward", lastname="Abbey")
|
||||
payment1.beneficiaries.add(bk, ea)
|
||||
|
||||
Order.objects.all().delete()
|
||||
|
||||
assert Order.objects.count() == 0
|
||||
assert Payment.objects.count() == 0
|
||||
assert set(Beneficiary.objects.all()) == {bk, ea}
|
||||
|
||||
def test_deletion_bug_540(self):
|
||||
"""
|
||||
https://github.com/jazzband/django-polymorphic/issues/540
|
||||
"""
|
||||
from .models import A_540, B_540
|
||||
|
||||
b = B_540.objects.create(self_referential=None, name="b")
|
||||
a = A_540.objects.create(self_referential=b)
|
||||
|
||||
A_540.objects.all().delete()
|
||||
|
||||
assert A_540.objects.count() == 0
|
||||
assert B_540.objects.count() == 0
|
||||
|
||||
def test_deletion_bug_547(self):
|
||||
"""
|
||||
https://github.com/jazzband/django-polymorphic/issues/547
|
||||
"""
|
||||
from .models import Project, DatasetFolder, OriginalFile, DatasetRelation, OriginalDataset
|
||||
|
||||
User = get_user_model()
|
||||
user = User.objects.create_user(username="u1", password="x")
|
||||
|
||||
project = Project.objects.create(name="p1", created_by=user)
|
||||
folder = DatasetFolder.objects.create(prjct=project)
|
||||
|
||||
rel_bytes = b"id,parent_id\n1,\n2,1\n"
|
||||
rel_file = ContentFile(rel_bytes, name="relations.csv")
|
||||
relation = DatasetRelation.objects.create(
|
||||
dataset_folder=folder,
|
||||
content_type="text/csv",
|
||||
size=len(rel_bytes),
|
||||
file=rel_file,
|
||||
original_file_name="relations.csv",
|
||||
)
|
||||
|
||||
ds_bytes = b"id,value\n1,foo\n2,bar\n"
|
||||
ds_file = ContentFile(ds_bytes, name="data.csv")
|
||||
OriginalDataset.objects.create(
|
||||
dataset_folder=folder,
|
||||
content_type="text/csv",
|
||||
size=len(ds_bytes),
|
||||
dataset_relation=relation,
|
||||
file=ds_file,
|
||||
original_file_name="data.csv",
|
||||
table_name="data",
|
||||
rows_number=2,
|
||||
dataset_metadata={"columns": ["id", "value"]},
|
||||
)
|
||||
|
||||
# This is the operation that (per report) can crash with:
|
||||
# AttributeError: 'NoneType' object has no attribute 'attname'
|
||||
#
|
||||
# If the bug is present, this test will error here.
|
||||
project.delete()
|
||||
|
||||
# If deletion succeeded, everything should be gone.
|
||||
assert Project.objects.count() == 0
|
||||
assert DatasetFolder.objects.count() == 0
|
||||
assert OriginalFile.objects.count() == 0
|
||||
assert DatasetRelation.objects.count() == 0
|
||||
assert OriginalDataset.objects.count() == 0
|
||||
|
||||
def test_deletion_bug_608(self):
|
||||
"""
|
||||
https://github.com/jazzband/django-polymorphic/issues/608
|
||||
"""
|
||||
from .models import (
|
||||
PolyDevice,
|
||||
PolyEthernetInterface,
|
||||
PolyFCInterface,
|
||||
PolyFixedInterface,
|
||||
PolyInterface,
|
||||
PolyModularInterface,
|
||||
PolyWirelessInterface,
|
||||
# NotPolyInterface
|
||||
)
|
||||
|
||||
device = PolyDevice.objects.create(name="Device 1")
|
||||
PolyEthernetInterface.objects.create(name="Eth0", device=device, ethernety_stuff="stuff")
|
||||
PolyFCInterface.objects.create(name="FC0", device=device, fc_stuff="stuff")
|
||||
PolyFixedInterface.objects.create(name="Fixed0", device=device, fixed_stuff="stuff")
|
||||
PolyModularInterface.objects.create(name="Modular0", device=device, modular_stuff="stuff")
|
||||
PolyWirelessInterface.objects.create(
|
||||
name="Wireless0", device=device, wirelessy_stuff="stuff"
|
||||
)
|
||||
PolyDevice.objects.all().delete()
|
||||
assert PolyDevice.objects.count() == 0
|
||||
|
||||
def test_deletion_bug_608_2(self):
|
||||
"""
|
||||
https://github.com/jazzband/django-polymorphic/issues/608
|
||||
"""
|
||||
from .models import Poll, Question, Answer, TextAnswer, YesNoAnswer
|
||||
|
||||
poll = Poll.objects.create()
|
||||
question = Question.objects.create(poll=poll)
|
||||
answer1 = TextAnswer.objects.create(question=question, answer="test")
|
||||
answer2 = YesNoAnswer.objects.create(question=question, answer=True)
|
||||
|
||||
poll.delete()
|
||||
|
||||
assert Poll.objects.count() == 0
|
||||
assert Question.objects.count() == 0
|
||||
assert Answer.objects.count() == 0
|
||||
assert TextAnswer.objects.count() == 0
|
||||
assert YesNoAnswer.objects.count() == 0
|
||||
|
||||
def test_vanilla_deletion(self):
|
||||
"""
|
||||
Test Django's vanilla multi table inheritance deletion and signaling.
|
||||
|
||||
PlainA *-----> Standalone
|
||||
/ \
|
||||
Standalone *------* PlainB1 PlainB2 *------> Standalone
|
||||
|
|
||||
PlainC1 *------> Standalone
|
||||
"""
|
||||
from .models import (
|
||||
PlainA,
|
||||
PlainB1,
|
||||
PlainC1,
|
||||
PlainB2,
|
||||
Standalone,
|
||||
RelatedToChild,
|
||||
Base,
|
||||
Child,
|
||||
GrandChild,
|
||||
RelatedToGrandChild,
|
||||
)
|
||||
|
||||
print("---------------------------")
|
||||
|
||||
PlainA.objects.create()
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
"""
|
||||
SELECT "plaina"."id", "plaina"."standalone_parent_id" FROM "plaina"
|
||||
SELECT "plainb1"."plaina_ptr_id" FROM "plainb1" WHERE "plainb1"."plaina_ptr_id" IN (1)
|
||||
DELETE FROM "plainb2" WHERE "plainb2"."plaina_ptr_id" IN (1)
|
||||
DELETE FROM "plaina" WHERE "plaina"."id" IN (1)
|
||||
"""
|
||||
PlainA.objects.all().delete()
|
||||
|
||||
# for q in ctx.captured_queries:
|
||||
# print(q["sql"])
|
||||
|
||||
print("---------------------------")
|
||||
PlainB1.objects.create()
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
"""
|
||||
SELECT "plaina"."id", "plaina"."standalone_parent_id", "plainb1"."plaina_ptr_id" FROM "plainb1" INNER JOIN "plaina" ON ("plainb1"."plaina_ptr_id" = "plaina"."id")
|
||||
SELECT "plainb1"."plaina_ptr_id", "plainc1"."plainb1_ptr_id" FROM "plainc1" INNER JOIN "plainb1" ON ("plainc1"."plainb1_ptr_id" = "plainb1"."plaina_ptr_id") WHERE "plainc1"."plainb1_ptr_id" IN (2)
|
||||
DELETE FROM "plainb1_standalones" WHERE "plainb1_standalones"."plainb1_id" IN (2)
|
||||
DELETE FROM "plainb1" WHERE "plainb1"."plaina_ptr_id" IN (2)
|
||||
DELETE FROM "plaina" WHERE "plaina"."id" IN (2)
|
||||
"""
|
||||
PlainB1.objects.all().delete()
|
||||
|
||||
# for q in ctx.captured_queries:
|
||||
# print(q["sql"])
|
||||
|
||||
print("---------------------------")
|
||||
PlainC1.objects.create()
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
"""
|
||||
SELECT "plaina"."id", "plaina"."standalone_parent_id", "plainb1"."plaina_ptr_id", "plainc1"."plainb1_ptr_id", "plainc1"."standalone_id" FROM "plainc1" INNER JOIN "plainb1" ON ("plainc1"."plainb1_ptr_id" = "plainb1"."plaina_ptr_id") INNER JOIN "plaina" ON ("plainb1"."plaina_ptr_id" = "plaina"."id")
|
||||
DELETE FROM "plainb1_standalones" WHERE "plainb1_standalones"."plainb1_id" IN (3)
|
||||
DELETE FROM "plainc1" WHERE "plainc1"."plainb1_ptr_id" IN (3)
|
||||
DELETE FROM "plainb1" WHERE "plainb1"."plaina_ptr_id" IN (3)
|
||||
DELETE FROM "plaina" WHERE "plaina"."id" IN (3)
|
||||
"""
|
||||
PlainC1.objects.all().delete()
|
||||
|
||||
# for q in ctx.captured_queries:
|
||||
# print(q["sql"])
|
||||
|
||||
print("---------------------------")
|
||||
PlainC1.objects.create()
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
"""
|
||||
SELECT "plaina"."id", "plaina"."standalone_parent_id" FROM "plaina"
|
||||
SELECT "plainb1"."plaina_ptr_id" FROM "plainb1" WHERE "plainb1"."plaina_ptr_id" IN (4)
|
||||
SELECT "plaina"."id", "plaina"."standalone_parent_id" FROM "plaina" WHERE "plaina"."id" = 4 LIMIT 21
|
||||
SELECT "plainb1"."plaina_ptr_id", "plainc1"."plainb1_ptr_id" FROM "plainc1" INNER JOIN "plainb1" ON ("plainc1"."plainb1_ptr_id" = "plainb1"."plaina_ptr_id") WHERE "plainc1"."plainb1_ptr_id" IN (4)
|
||||
SELECT "plaina"."id", "plaina"."standalone_parent_id", "plainb1"."plaina_ptr_id" FROM "plainb1" INNER JOIN "plaina" ON ("plainb1"."plaina_ptr_id" = "plaina"."id") WHERE "plainb1"."plaina_ptr_id" = 4 LIMIT 21
|
||||
DELETE FROM "plainb1_standalones" WHERE "plainb1_standalones"."plainb1_id" IN (4)
|
||||
DELETE FROM "plainb1_standalones" WHERE "plainb1_standalones"."plainb1_id" IN (4)
|
||||
DELETE FROM "plainb2" WHERE "plainb2"."plaina_ptr_id" IN (4)
|
||||
DELETE FROM "plainc1" WHERE "plainc1"."plainb1_ptr_id" IN (4)
|
||||
DELETE FROM "plainb1" WHERE "plainb1"."plaina_ptr_id" IN (4)
|
||||
DELETE FROM "plaina" WHERE "plaina"."id" IN (4)
|
||||
"""
|
||||
PlainA.objects.all().delete()
|
||||
|
||||
assert PlainC1.objects.count() == 0
|
||||
# for q in ctx.captured_queries:
|
||||
# print(q["sql"])
|
||||
|
||||
print("---------------------------")
|
||||
s0 = Standalone.objects.create()
|
||||
PlainC1.objects.create(standalone=s0)
|
||||
PlainB2.objects.create(standalone=s0)
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
"""
|
||||
SELECT "standalone"."id" FROM "standalone"
|
||||
SELECT "plaina"."id" FROM "plaina" WHERE "plaina"."standalone_parent_id" IN (1)
|
||||
SELECT "plaina"."id", "plaina"."standalone_parent_id", "plainb2"."plaina_ptr_id", "plainb2"."standalone_id" FROM "plainb2" INNER JOIN "plaina" ON ("plainb2"."plaina_ptr_id" = "plaina"."id") WHERE "plainb2"."standalone_id" IN (1)
|
||||
SELECT "plainb1"."plaina_ptr_id", "plainc1"."plainb1_ptr_id" FROM "plainc1" INNER JOIN "plainb1" ON ("plainc1"."plainb1_ptr_id" = "plainb1"."plaina_ptr_id") WHERE "plainc1"."standalone_id" IN (1)
|
||||
SELECT "plaina"."id", "plaina"."standalone_parent_id", "plainb1"."plaina_ptr_id" FROM "plainb1" INNER JOIN "plaina" ON ("plainb1"."plaina_ptr_id" = "plaina"."id") WHERE "plainb1"."plaina_ptr_id" = 5 LIMIT 21
|
||||
DELETE FROM "plainb1_standalones" WHERE "plainb1_standalones"."plainb1_id" IN (5)
|
||||
DELETE FROM "plainb1_standalones" WHERE "plainb1_standalones"."standalone_id" IN (1)
|
||||
DELETE FROM "standalone" WHERE "standalone"."id" IN (1)
|
||||
DELETE FROM "plainb2" WHERE "plainb2"."plaina_ptr_id" IN (6)
|
||||
DELETE FROM "plainc1" WHERE "plainc1"."plainb1_ptr_id" IN (5)
|
||||
DELETE FROM "plainb1" WHERE "plainb1"."plaina_ptr_id" IN (5)
|
||||
DELETE FROM "plaina" WHERE "plaina"."id" IN (6, 5)
|
||||
"""
|
||||
Standalone.objects.all().delete()
|
||||
|
||||
assert PlainC1.objects.count() == 0
|
||||
assert PlainB2.objects.count() == 0
|
||||
# for q in ctx.captured_queries:
|
||||
# print(q["sql"])
|
||||
|
||||
print("---------------------------")
|
||||
s0 = Standalone.objects.create()
|
||||
s1 = Standalone.objects.create()
|
||||
PlainB2.objects.create(standalone_parent=s0, standalone=s1)
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
"""
|
||||
SELECT "plaina"."id" FROM "plaina" WHERE "plaina"."standalone_parent_id" IN (2)
|
||||
SELECT "plainb1"."plaina_ptr_id" FROM "plainb1" WHERE "plainb1"."plaina_ptr_id" IN (7)
|
||||
SELECT "plaina"."id", "plaina"."standalone_parent_id", "plainb2"."plaina_ptr_id", "plainb2"."standalone_id" FROM "plainb2" INNER JOIN "plaina" ON ("plainb2"."plaina_ptr_id" = "plaina"."id") WHERE "plainb2"."standalone_id" IN (2)
|
||||
SELECT "plainb1"."plaina_ptr_id", "plainc1"."plainb1_ptr_id" FROM "plainc1" INNER JOIN "plainb1" ON ("plainc1"."plainb1_ptr_id" = "plainb1"."plaina_ptr_id") WHERE "plainc1"."standalone_id" IN (2)
|
||||
DELETE FROM "plainb2" WHERE "plainb2"."plaina_ptr_id" IN (7)
|
||||
DELETE FROM "plainb1_standalones" WHERE "plainb1_standalones"."standalone_id" IN (2)
|
||||
DELETE FROM "standalone" WHERE "standalone"."id" IN (2)
|
||||
DELETE FROM "plaina" WHERE "plaina"."id" IN (7)
|
||||
"""
|
||||
s0.delete()
|
||||
|
||||
assert PlainB2.objects.count() == 0
|
||||
assert list(Standalone.objects.all()) == [s1]
|
||||
# for q in ctx.captured_queries:
|
||||
# print(q["sql"])
|
||||
|
||||
print("---------------------------")
|
||||
grand_child = GrandChild.objects.create()
|
||||
RelatedToChild.objects.create(child=Child.objects.get(pk=grand_child.pk))
|
||||
RelatedToGrandChild.objects.create(grand_child=grand_child)
|
||||
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
"""
|
||||
SELECT "base"."id" FROM "base" WHERE "base"."id" = 1
|
||||
SELECT "child"."base_ptr_id" FROM "child" WHERE "child"."base_ptr_id" IN (1)
|
||||
SELECT "base"."id" FROM "base" WHERE "base"."id" = 1 LIMIT 21
|
||||
SELECT "child"."base_ptr_id", "grandchild"."child_ptr_id" FROM "grandchild" INNER JOIN "child" ON ("grandchild"."child_ptr_id" = "child"."base_ptr_id") WHERE "grandchild"."child_ptr_id" IN (1)
|
||||
SELECT "base"."id", "child"."base_ptr_id" FROM "child" INNER JOIN "base" ON ("child"."base_ptr_id" = "base"."id") WHERE "child"."base_ptr_id" = 1 LIMIT 21
|
||||
DELETE FROM "relatedtochild" WHERE "relatedtochild"."child_id" IN (1)
|
||||
DELETE FROM "relatedtograndchild" WHERE "relatedtograndchild"."grand_child_id" IN (1)
|
||||
DELETE FROM "relatedtochild" WHERE "relatedtochild"."child_id" IN (1)
|
||||
DELETE FROM "grandchild" WHERE "grandchild"."child_ptr_id" IN (1)
|
||||
DELETE FROM "child" WHERE "child"."base_ptr_id" IN (1)
|
||||
DELETE FROM "base" WHERE "base"."id" IN (1)
|
||||
"""
|
||||
Base.objects.filter(pk=grand_child.pk).delete() # cascade should reach relatives!
|
||||
|
||||
assert Base.objects.count() == 0
|
||||
assert Child.objects.count() == 0
|
||||
assert GrandChild.objects.count() == 0
|
||||
assert RelatedToChild.objects.count() == 0
|
||||
assert RelatedToGrandChild.objects.count() == 0
|
||||
# for q in ctx.captured_queries:
|
||||
# print(q["sql"])
|
||||
|
||||
def test_polymorphic_deletion_scenario1(self):
|
||||
"""
|
||||
Test the first polymorphic deletion scenario:
|
||||
|
||||
A normal model holds a foreign key to a polymorphic base model with several
|
||||
children.
|
||||
|
||||
<-- cascade --
|
||||
Normal1 ----- FK ----> Poly1
|
||||
/ | \
|
||||
A1 B1 C1
|
||||
|
||||
Tests that when you delete from a poly instance at any level of the
|
||||
poly hierarchy, cascading deletion propagates correctly to Normal1.
|
||||
|
||||
And deleting Normal1 also works with no effects on the poly instances.
|
||||
"""
|
||||
from .models import (
|
||||
Normal1,
|
||||
Poly1,
|
||||
A1,
|
||||
B1,
|
||||
C1,
|
||||
)
|
||||
|
||||
p1 = Poly1.objects.create()
|
||||
a1 = A1.objects.create()
|
||||
b1 = B1.objects.create()
|
||||
c1 = C1.objects.create()
|
||||
|
||||
n1 = Normal1.objects.create(poly=p1)
|
||||
n2 = Normal1.objects.create(poly=c1)
|
||||
|
||||
n3 = Normal1.objects.create(poly=a1)
|
||||
n4 = Normal1.objects.create(poly=b1)
|
||||
|
||||
n5 = Normal1.objects.create(poly=p1)
|
||||
n6 = Normal1.objects.create(poly=b1)
|
||||
|
||||
n7 = Normal1.objects.create(poly=b1)
|
||||
n8 = Normal1.objects.create(poly=c1)
|
||||
|
||||
# test delete from parent
|
||||
Poly1.objects.filter(pk=a1.pk).delete()
|
||||
assert Normal1.objects.count() == 7
|
||||
assert n3 not in Normal1.objects.all()
|
||||
assert A1.objects.count() == 0
|
||||
|
||||
Poly1.objects.filter(pk=p1.pk).delete()
|
||||
assert Normal1.objects.count() == 5
|
||||
assert n1 not in Normal1.objects.all()
|
||||
assert n5 not in Normal1.objects.all()
|
||||
|
||||
n6.delete()
|
||||
assert Normal1.objects.count() == 4
|
||||
assert B1.objects.count() == 1
|
||||
assert C1.objects.count() == 1
|
||||
assert b1 in Poly1.objects.all()
|
||||
assert c1 in Poly1.objects.all()
|
||||
|
||||
Poly1.objects.all().delete()
|
||||
assert Normal1.objects.count() == 0
|
||||
assert Poly1.objects.count() == 0
|
||||
|
||||
def test_polymorphic_deletion_scenario2(self):
|
||||
"""
|
||||
Test the second polymorphic deletion scenario:
|
||||
|
||||
A polymorphic model holds a foreign key to a base model with several
|
||||
children.
|
||||
|
||||
-- cascade -->
|
||||
Normal2 <----- FK ---- Poly2
|
||||
/ | \
|
||||
A2 B2 C2
|
||||
|
||||
Tests that when you delete a normal instance all related poly instances cascade
|
||||
correctly regardless of their concrete type in the hierarchy.
|
||||
|
||||
This is the major collector failure mode.
|
||||
"""
|
||||
|
||||
from .models import (
|
||||
Normal2,
|
||||
Poly2,
|
||||
A2,
|
||||
B2,
|
||||
C2,
|
||||
)
|
||||
|
||||
n1, n2, n3, n4 = (
|
||||
Normal2.objects.create(),
|
||||
Normal2.objects.create(),
|
||||
Normal2.objects.create(),
|
||||
Normal2.objects.create(),
|
||||
)
|
||||
|
||||
p1, p2 = Poly2.objects.create(normal=n1), Poly2.objects.create(normal=n4)
|
||||
a1, a2 = A2.objects.create(normal=n2), A2.objects.create(normal=n3)
|
||||
b1, b2 = B2.objects.create(normal=n4), B2.objects.create(normal=n2)
|
||||
c1, c2 = C2.objects.create(normal=n3), C2.objects.create(normal=n1)
|
||||
|
||||
assert set(n1.polies.all()) == {p1, c2}
|
||||
assert set(n2.polies.all()) == {a1, b2}
|
||||
assert set(n3.polies.all()) == {a2, c1}
|
||||
assert set(n4.polies.all()) == {p2, b1}
|
||||
|
||||
n2.delete()
|
||||
assert Poly2.objects.count() == 6
|
||||
assert a1 not in Poly2.objects.all()
|
||||
assert b2 not in Poly2.objects.all()
|
||||
|
||||
Normal2.objects.filter(pk__in=[n1.pk, n4.pk]).delete()
|
||||
assert Poly2.objects.count() == 2
|
||||
assert p1 not in Poly2.objects.all()
|
||||
assert c2 not in Poly2.objects.all()
|
||||
assert p2 not in Poly2.objects.all()
|
||||
assert b1 not in Poly2.objects.all()
|
||||
n3.delete()
|
||||
assert Poly2.objects.count() == 0
|
||||
|
||||
def test_polymorphic_deletion_scenario3(self):
|
||||
"""
|
||||
Scenario 3
|
||||
|
||||
Normal3
|
||||
|
|
||||
Poly3
|
||||
| \
|
||||
A3 B3
|
||||
|
||||
Deleting Poly3 should cascade delete Normal3 and deleting from Normal3 should
|
||||
cascade down to children.
|
||||
"""
|
||||
|
||||
from .models import (
|
||||
Normal3,
|
||||
Poly3,
|
||||
A3,
|
||||
B3,
|
||||
)
|
||||
|
||||
b1 = B3.objects.create()
|
||||
assert b1 in Poly3.objects.all()
|
||||
Normal3.objects.filter(pk=b1.pk).delete()
|
||||
assert b1 not in Poly3.objects.all()
|
||||
assert Poly3.objects.count() == 0
|
||||
|
||||
b2 = B3.objects.create()
|
||||
assert Normal3.objects.filter(pk=b2.pk).exists()
|
||||
assert b2 in Poly3.objects.all()
|
||||
b2.delete()
|
||||
assert not Normal3.objects.filter(pk=b2.pk).exists()
|
||||
assert Poly3.objects.count() == 0
|
||||
|
||||
def test_polymorphic_deletion_scenario4(self):
|
||||
"""
|
||||
Scenario 4 - M2Ms between normal/poly models
|
||||
|
||||
<--- cascade --->
|
||||
Normal4 <----- M2M -----> Poly4
|
||||
/ | \
|
||||
A4 B4 C4
|
||||
|
||||
Ensure relations are appropriately cascaded on deletions from either side.
|
||||
"""
|
||||
|
||||
from .models import (
|
||||
Normal4,
|
||||
Poly4,
|
||||
A4,
|
||||
B4,
|
||||
C4,
|
||||
)
|
||||
|
||||
n1, n2, n3, n4 = (
|
||||
Normal4.objects.create(),
|
||||
Normal4.objects.create(),
|
||||
Normal4.objects.create(),
|
||||
Normal4.objects.create(),
|
||||
)
|
||||
|
||||
p1, p2 = Poly4.objects.create(), Poly4.objects.create()
|
||||
a1, a2 = A4.objects.create(), A4.objects.create()
|
||||
b1, b2 = B4.objects.create(), B4.objects.create()
|
||||
c1, c2 = C4.objects.create(), C4.objects.create()
|
||||
|
||||
n1.polies.add(p1, a1, b1, c1)
|
||||
n2.polies.add(p2, a2, b2, c2)
|
||||
n3.polies.add(b1, c1)
|
||||
n4.polies.add(p2, c2)
|
||||
|
||||
assert set(n1.polies.all()) == {p1, a1, b1, c1}
|
||||
assert set(n2.polies.all()) == {p2, a2, b2, c2}
|
||||
assert set(n3.polies.all()) == {b1, c1}
|
||||
assert set(n4.polies.all()) == {p2, c2}
|
||||
|
||||
a1.delete()
|
||||
assert set(n1.polies.all()) == {p1, b1, c1}
|
||||
assert set(n2.polies.all()) == {p2, a2, b2, c2}
|
||||
assert set(n3.polies.all()) == {b1, c1}
|
||||
assert set(n4.polies.all()) == {p2, c2}
|
||||
|
||||
n4.delete()
|
||||
assert set(n1.polies.all()) == {p1, b1, c1}
|
||||
assert set(n2.polies.all()) == {p2, a2, b2, c2}
|
||||
assert set(n3.polies.all()) == {b1, c1}
|
||||
assert set(p2.normals.all()) == {n2}
|
||||
assert set(c2.normals.all()) == {n2}
|
||||
|
||||
Poly4.objects.all().delete()
|
||||
assert n1.polies.count() == 0
|
||||
assert n2.polies.count() == 0
|
||||
assert n3.polies.count() == 0
|
||||
|
||||
def test_polymorphic_deletion_scenario4_1(self):
|
||||
"""
|
||||
Scenario 4 - M2Ms between normal/poly models
|
||||
|
||||
<--- cascade --->
|
||||
Normal4_1 <----- M2M -----> Poly4
|
||||
/ | \
|
||||
A4 B4 C4
|
||||
|
||||
Ensure relations are appropriately cascaded on deletions from either side.
|
||||
"""
|
||||
|
||||
from .models import (
|
||||
Normal4_1,
|
||||
Poly4_1,
|
||||
A4_1,
|
||||
B4_1,
|
||||
C4_1,
|
||||
)
|
||||
|
||||
n1, n2, n3, n4 = (
|
||||
Normal4_1.objects.create(),
|
||||
Normal4_1.objects.create(),
|
||||
Normal4_1.objects.create(),
|
||||
Normal4_1.objects.create(),
|
||||
)
|
||||
|
||||
p1, p2 = Poly4_1.objects.create(), Poly4_1.objects.create()
|
||||
a1, a2 = A4_1.objects.create(), A4_1.objects.create()
|
||||
b1, b2 = B4_1.objects.create(), B4_1.objects.create()
|
||||
c1, c2 = C4_1.objects.create(), C4_1.objects.create()
|
||||
|
||||
n1.polies.add(p1, a1, b1, c1)
|
||||
n2.polies.add(p2, a2, b2, c2)
|
||||
n3.polies.add(b1, c1)
|
||||
n4.polies.add(p2, c2)
|
||||
|
||||
assert set(n1.polies.all()) == {p1, a1, b1, c1}
|
||||
assert set(n2.polies.all()) == {p2, a2, b2, c2}
|
||||
assert set(n3.polies.all()) == {b1, c1}
|
||||
assert set(n4.polies.all()) == {p2, c2}
|
||||
|
||||
a1.delete()
|
||||
assert set(n1.polies.all()) == {p1, b1, c1}
|
||||
assert set(n2.polies.all()) == {p2, a2, b2, c2}
|
||||
assert set(n3.polies.all()) == {b1, c1}
|
||||
assert set(n4.polies.all()) == {p2, c2}
|
||||
|
||||
n4.delete()
|
||||
assert set(n1.polies.all()) == {p1, b1, c1}
|
||||
assert set(n2.polies.all()) == {p2, a2, b2, c2}
|
||||
assert set(n3.polies.all()) == {b1, c1}
|
||||
assert set(p2.normals.all()) == {n2}
|
||||
assert set(c2.normals.all()) == {n2}
|
||||
|
||||
Poly4_1.objects.all().delete()
|
||||
assert n1.polies.count() == 0
|
||||
assert n2.polies.count() == 0
|
||||
assert n3.polies.count() == 0
|
||||
|
||||
def test_polymorphic_deletion_scenario5(self):
|
||||
"""
|
||||
Scenario 5 - scenario3 with custom/different PKs
|
||||
|
||||
Normal5
|
||||
|
|
||||
Poly5
|
||||
| \
|
||||
A5 B5
|
||||
|
||||
Deleting Poly5 should cascade delete Normal5 and deleting from Normal5 should
|
||||
cascade down to children.
|
||||
"""
|
||||
|
||||
from .models import (
|
||||
Normal5,
|
||||
Poly5,
|
||||
A5,
|
||||
B5,
|
||||
)
|
||||
|
||||
b1 = B5.objects.create()
|
||||
assert b1 in Poly5.objects.all()
|
||||
Normal5.objects.filter(pk=b1.pk).delete()
|
||||
assert b1 not in Poly5.objects.all()
|
||||
assert Poly5.objects.count() == 0
|
||||
|
||||
b2 = B5.objects.create()
|
||||
assert Normal5.objects.filter(pk=b2.pk).exists()
|
||||
assert b2 in Poly5.objects.all()
|
||||
b2.delete()
|
||||
assert not Normal5.objects.filter(pk=b2.pk).exists()
|
||||
assert Poly5.objects.count() == 0
|
||||
|
||||
# FIXME: django-polymorphic assumes all rows share the same PK value
|
||||
# n = Normal5.objects.create(n_pk=100)
|
||||
# p = Poly5.objects.create_from_super(n, p_pk=200)
|
||||
# A5.objects.create_from_super(p, a_pk=300)
|
||||
# b = B5.objects.create_from_super(p, b_pk=400)
|
||||
# assert Poly5.objects.count() == 1
|
||||
# assert b in Poly5.objects.all()
|
||||
# Normal5.objects.filter(pk=n.pk).delete()
|
||||
# assert Poly5.objects.count() == 0
|
||||
|
||||
# n1 = Normal5.objects.create(n_pk=101)
|
||||
# p1 = Poly5.objects.create_from_super(n1, p_pk=201)
|
||||
# A5.objects.create_from_super(p1, a_pk=301)
|
||||
# b1 = B5.objects.create_from_super(p1, b_pk=401)
|
||||
# assert Poly5.objects.count() == 1
|
||||
# assert b1 in Poly5.objects.all()
|
||||
# b1.delete()
|
||||
# assert Poly5.objects.count() == 0
|
||||
# assert Normal5.objects.count() == 0
|
||||
|
|
@ -32,3 +32,20 @@ class TestErrata(SimpleTestCase):
|
|||
assert any("not_instance_of" in msg for msg in error_messages), (
|
||||
f"Expected error for 'not_instance_of' field but got: {error_messages}"
|
||||
)
|
||||
|
||||
def test_polymorphic_guard_requires_callable(self):
|
||||
"""Test that PolymorphicGuard raises TypeError if initialized with non-callable."""
|
||||
|
||||
from polymorphic.deletion import PolymorphicGuard
|
||||
|
||||
non_callable_values = [42, "not a function", None, 3.14, [], {}]
|
||||
|
||||
for value in non_callable_values:
|
||||
try:
|
||||
PolymorphicGuard(value)
|
||||
except TypeError as e:
|
||||
assert str(e) == "action must be callable", (
|
||||
f"Expected TypeError with message 'action must be callable' but got: {e}"
|
||||
)
|
||||
else:
|
||||
assert False, f"Expected TypeError when initializing PolymorphicGuard with {value}"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 4.2 on 2025-12-16 21:55
|
||||
# Generated by Django 4.2 on 2025-12-20 11:18
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
|
|
|||
|
|
@ -100,6 +100,8 @@ INSTALLED_APPS = (
|
|||
"django.contrib.admin",
|
||||
"polymorphic",
|
||||
"polymorphic.tests",
|
||||
"polymorphic.tests.deletion",
|
||||
"polymorphic.tests.test_migrations",
|
||||
)
|
||||
|
||||
MIDDLEWARE = (
|
||||
|
|
|
|||
0
src/polymorphic/tests/test_migrations/__init__.py
Normal file
0
src/polymorphic/tests/test_migrations/__init__.py
Normal file
107
src/polymorphic/tests/test_migrations/models.py
Normal file
107
src/polymorphic/tests/test_migrations/models.py
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
from django.db import models
|
||||
from polymorphic.models import PolymorphicModel
|
||||
|
||||
|
||||
def get_default_related():
|
||||
"""Default function for SET() callable"""
|
||||
return None
|
||||
|
||||
|
||||
class RelatedModel(models.Model):
|
||||
"""A regular non-polymorphic model that will be referenced"""
|
||||
|
||||
name = models.CharField(max_length=100)
|
||||
|
||||
|
||||
class BasePolyModel(PolymorphicModel):
|
||||
"""
|
||||
Base polymorphic model to test that PolymorphicGuard wraps
|
||||
on_delete handlers properly and serializes them correctly.
|
||||
"""
|
||||
|
||||
name = models.CharField(max_length=100)
|
||||
|
||||
|
||||
class ChildPolyModel(BasePolyModel):
|
||||
"""Child polymorphic model"""
|
||||
|
||||
description = models.CharField(max_length=200, blank=True)
|
||||
|
||||
|
||||
class GrandChildPolyModel(ChildPolyModel):
|
||||
"""Grandchild polymorphic model"""
|
||||
|
||||
extra_info = models.CharField(max_length=200, blank=True)
|
||||
|
||||
|
||||
# Models with ForeignKey using different on_delete behaviors
|
||||
# These should all be wrapped with PolymorphicGuard automatically
|
||||
|
||||
|
||||
class ModelWithCascade(PolymorphicModel):
|
||||
"""Test CASCADE on_delete"""
|
||||
|
||||
related = models.ForeignKey(RelatedModel, on_delete=models.CASCADE)
|
||||
|
||||
|
||||
class ModelWithProtect(PolymorphicModel):
|
||||
"""Test PROTECT on_delete"""
|
||||
|
||||
related = models.ForeignKey(RelatedModel, on_delete=models.PROTECT)
|
||||
|
||||
|
||||
class ModelWithSetNull(PolymorphicModel):
|
||||
"""Test SET_NULL on_delete"""
|
||||
|
||||
related = models.ForeignKey(RelatedModel, on_delete=models.SET_NULL, null=True)
|
||||
|
||||
|
||||
class ModelWithSetDefault(PolymorphicModel):
|
||||
"""Test SET_DEFAULT on_delete"""
|
||||
|
||||
related = models.ForeignKey(
|
||||
RelatedModel, on_delete=models.SET_DEFAULT, null=True, default=None
|
||||
)
|
||||
|
||||
|
||||
class ModelWithSet(PolymorphicModel):
|
||||
"""Test SET(...) on_delete"""
|
||||
|
||||
related = models.ForeignKey(RelatedModel, on_delete=models.SET(get_default_related), null=True)
|
||||
|
||||
|
||||
class ModelWithDoNothing(PolymorphicModel):
|
||||
"""Test DO_NOTHING on_delete"""
|
||||
|
||||
related = models.ForeignKey(RelatedModel, on_delete=models.DO_NOTHING)
|
||||
|
||||
|
||||
class ModelWithRestrict(PolymorphicModel):
|
||||
"""Test RESTRICT on_delete"""
|
||||
|
||||
related = models.ForeignKey(RelatedModel, on_delete=models.RESTRICT)
|
||||
|
||||
|
||||
# OneToOneField tests
|
||||
|
||||
|
||||
class ModelWithOneToOneCascade(PolymorphicModel):
|
||||
"""Test CASCADE on_delete with OneToOneField"""
|
||||
|
||||
related = models.OneToOneField(RelatedModel, on_delete=models.CASCADE)
|
||||
|
||||
|
||||
class ModelWithOneToOneProtect(PolymorphicModel):
|
||||
"""Test PROTECT on_delete with OneToOneField"""
|
||||
|
||||
related = models.OneToOneField(
|
||||
RelatedModel, on_delete=models.PROTECT, related_name="one_to_one_protect"
|
||||
)
|
||||
|
||||
|
||||
class ModelWithOneToOneSetNull(PolymorphicModel):
|
||||
"""Test SET_NULL on_delete with OneToOneField"""
|
||||
|
||||
related = models.OneToOneField(
|
||||
RelatedModel, on_delete=models.SET_NULL, null=True, related_name="one_to_one_set_null"
|
||||
)
|
||||
584
src/polymorphic/tests/test_migrations/test_on_delete.py
Normal file
584
src/polymorphic/tests/test_migrations/test_on_delete.py
Normal file
|
|
@ -0,0 +1,584 @@
|
|||
"""
|
||||
Tests for PolymorphicGuard serialization of on_delete functions.
|
||||
|
||||
This test module ensures that all Django on_delete handlers (CASCADE, PROTECT,
|
||||
SET_NULL, SET_DEFAULT, SET(...), DO_NOTHING, and RESTRICT) are properly wrapped
|
||||
with PolymorphicGuard and serialize correctly in migrations.
|
||||
"""
|
||||
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from django.test import TestCase, TransactionTestCase
|
||||
from django.db import models
|
||||
from django.db.migrations.serializer import serializer_factory
|
||||
from django.db.models import ProtectedError, RestrictedError
|
||||
from ..utils import GeneratedMigrationsPerClassMixin
|
||||
from polymorphic.deletion import PolymorphicGuard
|
||||
from polymorphic.managers import PolymorphicManager
|
||||
from polymorphic.query import PolymorphicQuerySet
|
||||
|
||||
|
||||
class OnDeleteSerializationTest(GeneratedMigrationsPerClassMixin, TransactionTestCase):
|
||||
"""
|
||||
Test that PolymorphicGuard wraps on_delete handlers and serializes them correctly.
|
||||
"""
|
||||
|
||||
apps_to_migrate: list[str] = ["test_migrations"]
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
return self._applied_states["test_migrations"]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Set up by generating and applying migrations for test_migrations app"""
|
||||
super().setUpClass()
|
||||
cls.migrations_dir = Path(__file__).parent / "migrations"
|
||||
|
||||
def test_migration_managers_non_polymorphic(self):
|
||||
for mdl in [
|
||||
"BasePolyModel",
|
||||
"ChildPolyModel",
|
||||
"GrandChildPolyModel",
|
||||
"ModelWithCascade",
|
||||
"ModelWithProtect",
|
||||
"ModelWithSetNull",
|
||||
"ModelWithSetDefault",
|
||||
"ModelWithSet",
|
||||
"ModelWithDoNothing",
|
||||
"ModelWithRestrict",
|
||||
"ModelWithOneToOneCascade",
|
||||
"ModelWithOneToOneProtect",
|
||||
"ModelWithOneToOneSetNull",
|
||||
]:
|
||||
Model = self.state.apps.get_model("test_migrations", mdl)
|
||||
|
||||
managers = Model._meta.managers
|
||||
assert not isinstance(Model.objects, (PolymorphicManager, PolymorphicQuerySet))
|
||||
assert all(not isinstance(m, (PolymorphicManager, PolymorphicQuerySet)) for m in managers)
|
||||
|
||||
RelatedModel = self.state.apps.get_model("test_migrations", "RelatedModel")
|
||||
related = RelatedModel.objects.create(name="tester")
|
||||
ModelWithOneToOneCascade = self.state.apps.get_model(
|
||||
"test_migrations", "ModelWithOneToOneCascade"
|
||||
)
|
||||
ModelWithOneToOneProtect = self.state.apps.get_model(
|
||||
"test_migrations", "ModelWithOneToOneProtect"
|
||||
)
|
||||
ModelWithOneToOneSetNull = self.state.apps.get_model(
|
||||
"test_migrations", "ModelWithOneToOneSetNull"
|
||||
)
|
||||
|
||||
ModelWithOneToOneCascade.objects.create(related=related)
|
||||
ModelWithOneToOneProtect.objects.create(related=related)
|
||||
ModelWithOneToOneSetNull.objects.create(related=related)
|
||||
|
||||
for relation in [
|
||||
"modelwithcascade_set",
|
||||
"modelwithprotect_set",
|
||||
"modelwithsetnull_set",
|
||||
"modelwithsetdefault_set",
|
||||
"modelwithset_set",
|
||||
"modelwithdonothing_set",
|
||||
"modelwithrestrict_set",
|
||||
"modelwithonetoonecascade",
|
||||
"one_to_one_protect",
|
||||
"one_to_one_set_null",
|
||||
]:
|
||||
assert not isinstance(
|
||||
getattr(related, relation), (PolymorphicManager, PolymorphicQuerySet)
|
||||
)
|
||||
|
||||
def test_foreign_keys_wrapped_with_PolymorphicGuard(self):
|
||||
"""Verify that ForeignKey on_delete handlers are wrapped with PolymorphicGuard"""
|
||||
from .models import (
|
||||
ModelWithCascade,
|
||||
ModelWithProtect,
|
||||
ModelWithSetNull,
|
||||
ModelWithSetDefault,
|
||||
ModelWithSet,
|
||||
ModelWithDoNothing,
|
||||
ModelWithRestrict,
|
||||
)
|
||||
|
||||
# Get the 'related' field from each model
|
||||
models_to_test = [
|
||||
ModelWithCascade,
|
||||
ModelWithProtect,
|
||||
ModelWithSetNull,
|
||||
ModelWithSetDefault,
|
||||
ModelWithSet,
|
||||
ModelWithDoNothing,
|
||||
ModelWithRestrict,
|
||||
]
|
||||
|
||||
for model_class in models_to_test:
|
||||
with self.subTest(model=model_class.__name__):
|
||||
field = model_class._meta.get_field("related")
|
||||
on_delete = field.remote_field.on_delete
|
||||
|
||||
# Assert that the on_delete handler is wrapped with PolymorphicGuard
|
||||
self.assertIsInstance(
|
||||
on_delete,
|
||||
PolymorphicGuard,
|
||||
f"{model_class.__name__}.related field should have PolymorphicGuard wrapper",
|
||||
)
|
||||
|
||||
def test_one_to_one_wrapped_with_PolymorphicGuard(self):
|
||||
"""Verify that OneToOneField on_delete handlers are wrapped with PolymorphicGuard"""
|
||||
from .models import (
|
||||
ModelWithOneToOneCascade,
|
||||
ModelWithOneToOneProtect,
|
||||
ModelWithOneToOneSetNull,
|
||||
)
|
||||
|
||||
models_to_test = [
|
||||
ModelWithOneToOneCascade,
|
||||
ModelWithOneToOneProtect,
|
||||
ModelWithOneToOneSetNull,
|
||||
]
|
||||
|
||||
for model_class in models_to_test:
|
||||
with self.subTest(model=model_class.__name__):
|
||||
field = model_class._meta.get_field("related")
|
||||
on_delete = field.remote_field.on_delete
|
||||
|
||||
# Assert that the on_delete handler is wrapped with PolymorphicGuard
|
||||
self.assertIsInstance(
|
||||
on_delete,
|
||||
PolymorphicGuard,
|
||||
f"{model_class.__name__}.related field should have PolymorphicGuard wrapper",
|
||||
)
|
||||
|
||||
def test_cascade_serialization(self):
|
||||
"""Test that CASCADE serializes correctly through PolymorphicGuard"""
|
||||
from .models import ModelWithCascade
|
||||
|
||||
field = ModelWithCascade._meta.get_field("related")
|
||||
on_delete = field.remote_field.on_delete
|
||||
|
||||
# Serialize the PolymorphicGuard wrapped CASCADE
|
||||
serialized, imports = serializer_factory(on_delete).serialize()
|
||||
|
||||
# Should serialize as CASCADE from django.db.models.deletion, not as PolymorphicGuard
|
||||
self.assertIn("CASCADE", serialized)
|
||||
self.assertNotIn("PolymorphicGuard", serialized)
|
||||
|
||||
def test_protect_serialization(self):
|
||||
"""Test that PROTECT serializes correctly through PolymorphicGuard"""
|
||||
from .models import ModelWithProtect
|
||||
|
||||
field = ModelWithProtect._meta.get_field("related")
|
||||
on_delete = field.remote_field.on_delete
|
||||
|
||||
serialized, imports = serializer_factory(on_delete).serialize()
|
||||
|
||||
self.assertIn("PROTECT", serialized)
|
||||
self.assertNotIn("PolymorphicGuard", serialized)
|
||||
|
||||
def test_set_null_serialization(self):
|
||||
"""Test that SET_NULL serializes correctly through PolymorphicGuard"""
|
||||
from .models import ModelWithSetNull
|
||||
|
||||
field = ModelWithSetNull._meta.get_field("related")
|
||||
on_delete = field.remote_field.on_delete
|
||||
|
||||
serialized, imports = serializer_factory(on_delete).serialize()
|
||||
|
||||
self.assertIn("SET_NULL", serialized)
|
||||
self.assertNotIn("PolymorphicGuard", serialized)
|
||||
|
||||
def test_set_default_serialization(self):
|
||||
"""Test that SET_DEFAULT serializes correctly through PolymorphicGuard"""
|
||||
from .models import ModelWithSetDefault
|
||||
|
||||
field = ModelWithSetDefault._meta.get_field("related")
|
||||
on_delete = field.remote_field.on_delete
|
||||
|
||||
serialized, imports = serializer_factory(on_delete).serialize()
|
||||
|
||||
self.assertIn("SET_DEFAULT", serialized)
|
||||
self.assertNotIn("PolymorphicGuard", serialized)
|
||||
|
||||
def test_set_callable_serialization(self):
|
||||
"""Test that SET(...) with a callable serializes correctly through PolymorphicGuard"""
|
||||
from .models import ModelWithSet
|
||||
|
||||
field = ModelWithSet._meta.get_field("related")
|
||||
on_delete = field.remote_field.on_delete
|
||||
|
||||
serialized, imports = serializer_factory(on_delete).serialize()
|
||||
|
||||
# Should serialize the SET() function with the callable reference
|
||||
self.assertIn("SET", serialized)
|
||||
self.assertIn("get_default_related", serialized)
|
||||
self.assertNotIn("PolymorphicGuard", serialized)
|
||||
|
||||
def test_do_nothing_serialization(self):
|
||||
"""Test that DO_NOTHING serializes correctly through PolymorphicGuard"""
|
||||
from .models import ModelWithDoNothing
|
||||
|
||||
field = ModelWithDoNothing._meta.get_field("related")
|
||||
on_delete = field.remote_field.on_delete
|
||||
|
||||
serialized, imports = serializer_factory(on_delete).serialize()
|
||||
|
||||
self.assertIn("DO_NOTHING", serialized)
|
||||
self.assertNotIn("PolymorphicGuard", serialized)
|
||||
|
||||
def test_restrict_serialization(self):
|
||||
"""Test that RESTRICT serializes correctly through PolymorphicGuard"""
|
||||
from .models import ModelWithRestrict
|
||||
|
||||
field = ModelWithRestrict._meta.get_field("related")
|
||||
on_delete = field.remote_field.on_delete
|
||||
|
||||
serialized, imports = serializer_factory(on_delete).serialize()
|
||||
|
||||
self.assertIn("RESTRICT", serialized)
|
||||
self.assertNotIn("PolymorphicGuard", serialized)
|
||||
|
||||
def test_migration_file_generated(self):
|
||||
"""Test that a migration file was generated"""
|
||||
# Check that at least one migration file was created
|
||||
migration_files = list(self.migrations_dir.glob("0001_*.py"))
|
||||
self.assertTrue(len(migration_files) > 0, "No migration file was generated")
|
||||
|
||||
def test_migration_file_content(self):
|
||||
"""Test that the generated migration file contains correct serialization"""
|
||||
# Find the initial migration file
|
||||
migration_files = list(self.migrations_dir.glob("0001_*.py"))
|
||||
self.assertTrue(len(migration_files) > 0, "No migration file found")
|
||||
|
||||
migration_file = migration_files[0]
|
||||
content = migration_file.read_text()
|
||||
|
||||
# Check that PolymorphicGuard is NOT in the migration file
|
||||
self.assertNotIn(
|
||||
"PolymorphicGuard", content, "Migration file should not contain PolymorphicGuard"
|
||||
)
|
||||
|
||||
# Check that on_delete handlers are properly serialized
|
||||
self.assertIn("django.db.models.deletion.CASCADE", content)
|
||||
self.assertIn("django.db.models.deletion.PROTECT", content)
|
||||
self.assertIn("django.db.models.deletion.SET_NULL", content)
|
||||
self.assertIn("django.db.models.deletion.SET_DEFAULT", content)
|
||||
self.assertIn("django.db.models.deletion.DO_NOTHING", content)
|
||||
self.assertIn("django.db.models.deletion.RESTRICT", content)
|
||||
|
||||
# Check that SET() with callable is properly serialized
|
||||
self.assertIn("models.SET", content)
|
||||
self.assertIn("get_default_related", content)
|
||||
|
||||
def test_migration_serialization_stability(self):
|
||||
"""
|
||||
Test that the migration file contains stable serialization.
|
||||
This ensures that PolymorphicGuard doesn't cause migration churn
|
||||
by verifying the migration was generated successfully in setUpClass.
|
||||
"""
|
||||
# The fact that we have a migration file and it contains the right
|
||||
# serialization (tested in test_migration_file_content) proves
|
||||
# that the serialization is stable. If it wasn't stable, the
|
||||
# migration file would either fail to generate or contain
|
||||
# PolymorphicGuard references.
|
||||
migration_files = list(self.migrations_dir.glob("0001_*.py"))
|
||||
self.assertEqual(len(migration_files), 1, "Should have exactly one initial migration file")
|
||||
|
||||
def test_PolymorphicGuard_unwraps_correctly(self):
|
||||
"""Test that PolymorphicGuard properly unwraps to the underlying action"""
|
||||
from .models import ModelWithCascade
|
||||
|
||||
field = ModelWithCascade._meta.get_field("related")
|
||||
on_delete = field.remote_field.on_delete
|
||||
|
||||
# Verify it's wrapped
|
||||
self.assertIsInstance(on_delete, PolymorphicGuard)
|
||||
|
||||
# Verify the underlying action is CASCADE
|
||||
self.assertEqual(on_delete.action, models.CASCADE)
|
||||
|
||||
def test_all_on_delete_types_covered(self):
|
||||
"""
|
||||
Meta-test to ensure we've covered all Django on_delete types.
|
||||
This test documents which on_delete types we're testing.
|
||||
"""
|
||||
tested_types = {
|
||||
"CASCADE": models.CASCADE,
|
||||
"PROTECT": models.PROTECT,
|
||||
"SET_NULL": models.SET_NULL,
|
||||
"SET_DEFAULT": models.SET_DEFAULT,
|
||||
"SET": models.SET, # This is a callable that returns the actual handler
|
||||
"DO_NOTHING": models.DO_NOTHING,
|
||||
"RESTRICT": models.RESTRICT,
|
||||
}
|
||||
|
||||
# Document that we have test models for each type
|
||||
from .models import (
|
||||
ModelWithCascade,
|
||||
ModelWithProtect,
|
||||
ModelWithSetNull,
|
||||
ModelWithSetDefault,
|
||||
ModelWithSet,
|
||||
ModelWithDoNothing,
|
||||
ModelWithRestrict,
|
||||
)
|
||||
|
||||
model_mapping = {
|
||||
"CASCADE": ModelWithCascade,
|
||||
"PROTECT": ModelWithProtect,
|
||||
"SET_NULL": ModelWithSetNull,
|
||||
"SET_DEFAULT": ModelWithSetDefault,
|
||||
"SET": ModelWithSet,
|
||||
"DO_NOTHING": ModelWithDoNothing,
|
||||
"RESTRICT": ModelWithRestrict,
|
||||
}
|
||||
|
||||
# Verify we have a model for each on_delete type
|
||||
for type_name, on_delete_handler in tested_types.items():
|
||||
with self.subTest(type=type_name):
|
||||
self.assertIn(type_name, model_mapping, f"Missing test model for {type_name}")
|
||||
model_class = model_mapping[type_name]
|
||||
field = model_class._meta.get_field("related")
|
||||
|
||||
# Verify the field is properly configured
|
||||
self.assertIsNotNone(field)
|
||||
self.assertIsInstance(field.remote_field.on_delete, PolymorphicGuard)
|
||||
|
||||
|
||||
class PolymorphicInheritanceSerializationTest(TestCase):
|
||||
"""
|
||||
Test that PolymorphicGuard works correctly with polymorphic model inheritance.
|
||||
"""
|
||||
|
||||
def test_polymorphic_inheritance_chain(self):
|
||||
"""Test that polymorphic model inheritance works with all on_delete types"""
|
||||
from .models import BasePolyModel, ChildPolyModel, GrandChildPolyModel
|
||||
|
||||
# Verify the inheritance chain is set up correctly
|
||||
self.assertTrue(issubclass(ChildPolyModel, BasePolyModel))
|
||||
self.assertTrue(issubclass(GrandChildPolyModel, ChildPolyModel))
|
||||
self.assertTrue(issubclass(GrandChildPolyModel, BasePolyModel))
|
||||
|
||||
# Verify each model has the polymorphic_ctype field
|
||||
for model_class in [BasePolyModel, ChildPolyModel, GrandChildPolyModel]:
|
||||
with self.subTest(model=model_class.__name__):
|
||||
ctype_field = model_class._meta.get_field("polymorphic_ctype")
|
||||
self.assertIsNotNone(ctype_field)
|
||||
# The polymorphic_ctype field uses CASCADE which should also be wrapped
|
||||
self.assertIsInstance(ctype_field.remote_field.on_delete, PolymorphicGuard)
|
||||
|
||||
|
||||
class OnDeleteBehaviorTest(GeneratedMigrationsPerClassMixin, TransactionTestCase):
|
||||
"""
|
||||
Test that PolymorphicGuard correctly executes on_delete actions.
|
||||
|
||||
These tests verify the runtime behavior of each on_delete type when
|
||||
wrapped with PolymorphicGuard by creating and deleting model instances.
|
||||
"""
|
||||
|
||||
apps_to_migrate: list[str] = ["test_migrations"]
|
||||
|
||||
def test_cascade_deletes_related_objects(self):
|
||||
"""Test that CASCADE deletes related polymorphic objects"""
|
||||
from .models import RelatedModel, ModelWithCascade
|
||||
|
||||
# Create a related model and a polymorphic model that references it
|
||||
related = RelatedModel.objects.create(name="test")
|
||||
cascade_obj = ModelWithCascade.objects.create(related=related)
|
||||
cascade_obj_id = cascade_obj.id
|
||||
|
||||
# Verify the object exists
|
||||
self.assertTrue(ModelWithCascade.objects.filter(id=cascade_obj_id).exists())
|
||||
|
||||
# Delete the related model
|
||||
related.delete()
|
||||
|
||||
# Verify the cascade object was deleted
|
||||
self.assertFalse(ModelWithCascade.objects.filter(id=cascade_obj_id).exists())
|
||||
|
||||
def test_protect_prevents_deletion(self):
|
||||
"""Test that PROTECT prevents deletion of related objects"""
|
||||
from .models import RelatedModel, ModelWithProtect
|
||||
|
||||
# Create a related model and a polymorphic model that references it
|
||||
related = RelatedModel.objects.create(name="test")
|
||||
ModelWithProtect.objects.create(related=related)
|
||||
|
||||
# Attempting to delete the related model should raise ProtectedError
|
||||
with self.assertRaises(ProtectedError):
|
||||
related.delete()
|
||||
|
||||
# Verify both objects still exist
|
||||
self.assertTrue(RelatedModel.objects.filter(id=related.id).exists())
|
||||
self.assertTrue(ModelWithProtect.objects.filter(related=related).exists())
|
||||
|
||||
def test_set_null_sets_field_to_null(self):
|
||||
"""Test that SET_NULL sets the foreign key to null"""
|
||||
from .models import RelatedModel, ModelWithSetNull
|
||||
|
||||
# Create a related model and a polymorphic model that references it
|
||||
related = RelatedModel.objects.create(name="test")
|
||||
set_null_obj = ModelWithSetNull.objects.create(related=related)
|
||||
set_null_obj_id = set_null_obj.id
|
||||
|
||||
# Verify the relationship exists
|
||||
self.assertEqual(set_null_obj.related, related)
|
||||
|
||||
# Delete the related model
|
||||
related.delete()
|
||||
|
||||
# Verify the object still exists but the field is now null
|
||||
set_null_obj = ModelWithSetNull.objects.get(id=set_null_obj_id)
|
||||
self.assertIsNone(set_null_obj.related)
|
||||
|
||||
def test_set_default_sets_field_to_default(self):
|
||||
"""Test that SET_DEFAULT sets the foreign key to its default value"""
|
||||
from .models import RelatedModel, ModelWithSetDefault
|
||||
|
||||
# Create a related model and a polymorphic model that references it
|
||||
related = RelatedModel.objects.create(name="test")
|
||||
set_default_obj = ModelWithSetDefault.objects.create(related=related)
|
||||
set_default_obj_id = set_default_obj.id
|
||||
|
||||
# Verify the relationship exists
|
||||
self.assertEqual(set_default_obj.related, related)
|
||||
|
||||
# Delete the related model
|
||||
related.delete()
|
||||
|
||||
# Verify the object still exists but the field is now set to default (None)
|
||||
set_default_obj = ModelWithSetDefault.objects.get(id=set_default_obj_id)
|
||||
self.assertIsNone(set_default_obj.related)
|
||||
|
||||
def test_set_callable_uses_function(self):
|
||||
"""Test that SET(...) calls the provided function"""
|
||||
from .models import RelatedModel, ModelWithSet
|
||||
|
||||
# Create a related model and a polymorphic model that references it
|
||||
related = RelatedModel.objects.create(name="test")
|
||||
set_obj = ModelWithSet.objects.create(related=related)
|
||||
set_obj_id = set_obj.id
|
||||
|
||||
# Verify the relationship exists
|
||||
self.assertEqual(set_obj.related, related)
|
||||
|
||||
# Delete the related model
|
||||
related.delete()
|
||||
|
||||
# Verify the object s
|
||||
set_obj = ModelWithSet.objects.get(id=set_obj_id)
|
||||
self.assertIsNone(set_obj.related)
|
||||
|
||||
def test_do_nothing_behavior(self):
|
||||
"""Test that DO_NOTHING doesn't prevent deletion or update related objects"""
|
||||
from .models import RelatedModel, ModelWithDoNothing
|
||||
|
||||
# Create a related model and a polymorphic model that references it
|
||||
related = RelatedModel.objects.create(name="test")
|
||||
do_nothing_obj = ModelWithDoNothing.objects.create(related=related)
|
||||
|
||||
# Verify the object is wrapped with PolymorphicGuard
|
||||
field = ModelWithDoNothing._meta.get_field("related")
|
||||
self.assertIsInstance(field.remote_field.on_delete, PolymorphicGuard)
|
||||
|
||||
# Verify the underlying action is DO_NOTHING
|
||||
self.assertEqual(field.remote_field.on_delete.action, models.DO_NOTHING)
|
||||
|
||||
# DO_NOTHING doesn't cascade delete or set null - it simply does nothing
|
||||
# In practice, this means the deletion succeeds but leaves an orphaned reference
|
||||
# However, database constraints may prevent this in production
|
||||
# Here we just verify that the wrapper is correct and the object exists
|
||||
self.assertTrue(ModelWithDoNothing.objects.filter(id=do_nothing_obj.id).exists())
|
||||
self.assertEqual(do_nothing_obj.related, related)
|
||||
|
||||
def test_restrict_prevents_deletion_when_objects_exist(self):
|
||||
"""Test that RESTRICT prevents deletion when related objects exist"""
|
||||
from .models import RelatedModel, ModelWithRestrict
|
||||
|
||||
# Create a related model and a polymorphic model that references it
|
||||
related = RelatedModel.objects.create(name="test")
|
||||
restrict_obj = ModelWithRestrict.objects.create(related=related)
|
||||
|
||||
# Attempting to delete the related model should raise RestrictedError
|
||||
with self.assertRaises(RestrictedError):
|
||||
related.delete()
|
||||
|
||||
# Verify both objects still exist
|
||||
self.assertTrue(RelatedModel.objects.filter(id=related.id).exists())
|
||||
self.assertTrue(ModelWithRestrict.objects.filter(id=restrict_obj.id).exists())
|
||||
|
||||
def test_cascade_with_polymorphic_inheritance(self):
|
||||
"""Test CASCADE works correctly with polymorphic child models"""
|
||||
from .models import RelatedModel, ModelWithCascade
|
||||
|
||||
# Create a related model
|
||||
related = RelatedModel.objects.create(name="test")
|
||||
|
||||
# Create multiple instances of the polymorphic model
|
||||
obj1 = ModelWithCascade.objects.create(related=related)
|
||||
obj2 = ModelWithCascade.objects.create(related=related)
|
||||
obj1_id, obj2_id = obj1.id, obj2.id
|
||||
|
||||
# Verify they exist
|
||||
self.assertEqual(ModelWithCascade.objects.filter(related=related).count(), 2)
|
||||
|
||||
# Delete the related model
|
||||
related.delete()
|
||||
|
||||
# Verify all cascade objects were deleted
|
||||
self.assertFalse(ModelWithCascade.objects.filter(id=obj1_id).exists())
|
||||
self.assertFalse(ModelWithCascade.objects.filter(id=obj2_id).exists())
|
||||
self.assertEqual(ModelWithCascade.objects.count(), 0)
|
||||
|
||||
def test_one_to_one_cascade_deletes_related_object(self):
|
||||
"""Test CASCADE with OneToOneField deletes related polymorphic object"""
|
||||
from .models import RelatedModel, ModelWithOneToOneCascade
|
||||
|
||||
# Create a related model and a polymorphic model with OneToOne
|
||||
related = RelatedModel.objects.create(name="test")
|
||||
one_to_one_obj = ModelWithOneToOneCascade.objects.create(related=related)
|
||||
one_to_one_obj_id = one_to_one_obj.id
|
||||
|
||||
# Verify the object exists
|
||||
self.assertTrue(ModelWithOneToOneCascade.objects.filter(id=one_to_one_obj_id).exists())
|
||||
|
||||
# Delete the related model
|
||||
related.delete()
|
||||
|
||||
# Verify the one-to-one object was deleted
|
||||
self.assertFalse(ModelWithOneToOneCascade.objects.filter(id=one_to_one_obj_id).exists())
|
||||
|
||||
def test_one_to_one_protect_prevents_deletion(self):
|
||||
"""Test PROTECT with OneToOneField prevents deletion"""
|
||||
from .models import RelatedModel, ModelWithOneToOneProtect
|
||||
|
||||
# Create a related model and a polymorphic model with OneToOne
|
||||
related = RelatedModel.objects.create(name="test")
|
||||
ModelWithOneToOneProtect.objects.create(related=related)
|
||||
|
||||
# Attempting to delete should raise ProtectedError
|
||||
with self.assertRaises(ProtectedError):
|
||||
related.delete()
|
||||
|
||||
# Verify both objects still exist
|
||||
self.assertTrue(RelatedModel.objects.filter(id=related.id).exists())
|
||||
self.assertTrue(ModelWithOneToOneProtect.objects.filter(related=related).exists())
|
||||
|
||||
def test_one_to_one_set_null_sets_to_null(self):
|
||||
"""Test SET_NULL with OneToOneField sets field to null"""
|
||||
from .models import RelatedModel, ModelWithOneToOneSetNull
|
||||
|
||||
# Create a related model and a polymorphic model with OneToOne
|
||||
related = RelatedModel.objects.create(name="test")
|
||||
one_to_one_obj = ModelWithOneToOneSetNull.objects.create(related=related)
|
||||
one_to_one_obj_id = one_to_one_obj.id
|
||||
|
||||
# Verify the relationship exists
|
||||
self.assertEqual(one_to_one_obj.related, related)
|
||||
|
||||
# Delete the related model
|
||||
related.delete()
|
||||
|
||||
# Verify the object still exists but the field is now null
|
||||
one_to_one_obj = ModelWithOneToOneSetNull.objects.get(id=one_to_one_obj_id)
|
||||
self.assertIsNone(one_to_one_obj.related)
|
||||
|
|
@ -1358,7 +1358,7 @@ class PolymorphicTests(TransactionTestCase):
|
|||
assert False, "Unexpected model type"
|
||||
assert (b, c, d) == (250, 1000, 2000)
|
||||
|
||||
assert len(poly_all) <= 7, (
|
||||
assert len(poly_all) <= 8, (
|
||||
f"Expected < 7 queries for chunked iteration over 3250 "
|
||||
f"objects with 3 child models and the default chunk size of 2000, encountered "
|
||||
f"{len(poly_all)}"
|
||||
|
|
@ -1450,23 +1450,16 @@ class PolymorphicTests(TransactionTestCase):
|
|||
if connection.vendor == "postgresql":
|
||||
assert len(poly_chunked) == 4, "On postgres with a 4000 chunk size, expected 4 queries"
|
||||
|
||||
try:
|
||||
result = Model2A.objects.all().delete()
|
||||
assert result == (
|
||||
11500,
|
||||
{
|
||||
"tests.Model2D": 2000,
|
||||
"tests.Model2C": 3000,
|
||||
"tests.Model2A": 3250,
|
||||
"tests.Model2B": 3250,
|
||||
},
|
||||
)
|
||||
except AttributeError:
|
||||
if connection.vendor == "oracle":
|
||||
# FIXME
|
||||
# known deletion issue with oracle
|
||||
# https://github.com/jazzband/django-polymorphic/issues/673
|
||||
pass
|
||||
result = Model2A.objects.all().delete()
|
||||
assert result == (
|
||||
11500,
|
||||
{
|
||||
"tests.Model2D": 2000,
|
||||
"tests.Model2C": 3000,
|
||||
"tests.Model2A": 3250,
|
||||
"tests.Model2B": 3250,
|
||||
},
|
||||
)
|
||||
|
||||
def test_transmogrify_with_init(self):
|
||||
pur = PurpleHeadDuck.objects.create()
|
||||
|
|
|
|||
87
src/polymorphic/tests/utils.py
Normal file
87
src/polymorphic/tests/utils.py
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
import io
|
||||
|
||||
from django.core.management import call_command
|
||||
|
||||
from django_test_migrations.migrator import Migrator
|
||||
from django.apps import apps
|
||||
|
||||
|
||||
class GeneratedMigrationsPerClassMixin:
|
||||
"""
|
||||
Generates migrations at class setup, applies them, and rolls them back at teardown.
|
||||
|
||||
Configure:
|
||||
- apps_to_migrate = ["my_app", ...]
|
||||
- database = "default" (optional)
|
||||
"""
|
||||
|
||||
apps_to_migrate: list[str] = []
|
||||
database: str = "default"
|
||||
settings: str = os.environ.get("DJANGO_SETTINGS_MODULE", "polymorphic.tests.settings")
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
if not cls.apps_to_migrate:
|
||||
raise RuntimeError("Set apps_to_migrate = ['your_app', ...]")
|
||||
|
||||
for app_label in cls.apps_to_migrate:
|
||||
call_command(
|
||||
"makemigrations",
|
||||
app_label,
|
||||
interactive=False,
|
||||
verbosity=0,
|
||||
)
|
||||
|
||||
# 2) Apply all migrations (up to latest) using django-test-migrations
|
||||
cls.migrator = Migrator(database=cls.database)
|
||||
|
||||
cls._applied_states = {}
|
||||
for app_label in cls.apps_to_migrate:
|
||||
latest = cls._find_latest_migration_name(app_label)
|
||||
# apply_initial_migration applies all migrations up to and including `latest`
|
||||
cls._applied_states[app_label] = cls.migrator.apply_initial_migration(
|
||||
(app_label, latest)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
try:
|
||||
# Roll everything back / cleanup:
|
||||
if hasattr(cls, "migrator"):
|
||||
cls.migrator.reset()
|
||||
finally:
|
||||
# remove files
|
||||
for app_label in cls.apps_to_migrate:
|
||||
app_config = apps.get_app_config(app_label) # app *label*
|
||||
mig_dir = Path(app_config.path) / "migrations"
|
||||
|
||||
for mig_file in mig_dir.glob("*.py"):
|
||||
if mig_file.name != "__init__.py" and mig_file.name[0:4].isdigit():
|
||||
os.remove(mig_file)
|
||||
|
||||
# also remove __pycache__ if exists
|
||||
pycache_dir = mig_dir / "__pycache__"
|
||||
if pycache_dir.exists() and pycache_dir.is_dir():
|
||||
shutil.rmtree(pycache_dir)
|
||||
|
||||
super().tearDownClass()
|
||||
|
||||
@classmethod
|
||||
def _find_latest_migration_name(cls, app_label: str) -> str:
|
||||
"""
|
||||
Returns "000X_..." latest migration filename (without .py).
|
||||
"""
|
||||
app_config = apps.get_app_config(app_label) # app *label*
|
||||
mig_dir = Path(app_config.path) / "migrations"
|
||||
|
||||
candidates = sorted(
|
||||
p for p in mig_dir.glob("*.py") if p.name != "__init__.py" and p.name[0:4].isdigit()
|
||||
)
|
||||
if not candidates:
|
||||
raise RuntimeError(f"No migrations generated for {app_label}")
|
||||
return candidates[-1].stem
|
||||
364
uv.lock
364
uv.lock
|
|
@ -87,11 +87,11 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "cachetools"
|
||||
version = "6.2.2"
|
||||
version = "6.2.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fb/44/ca1675be2a83aeee1886ab745b28cda92093066590233cc501890eb8417a/cachetools-6.2.2.tar.gz", hash = "sha256:8e6d266b25e539df852251cfd6f990b4bc3a141db73b939058d809ebd2590fc6", size = 31571, upload-time = "2025-11-13T17:42:51.465Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bc/1d/ede8680603f6016887c062a2cf4fc8fdba905866a3ab8831aa8aa651320c/cachetools-6.2.4.tar.gz", hash = "sha256:82c5c05585e70b6ba2d3ae09ea60b79548872185d2f24ae1f2709d37299fd607", size = 31731, upload-time = "2025-12-15T18:24:53.744Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/46/eb6eca305c77a4489affe1c5d8f4cae82f285d9addd8de4ec084a7184221/cachetools-6.2.2-py3-none-any.whl", hash = "sha256:6c09c98183bf58560c97b2abfcedcbaf6a896a490f534b031b661d3723b45ace", size = 11503, upload-time = "2025-11-13T17:42:50.232Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/fc/1d7b80d0eb7b714984ce40efc78859c022cd930e402f599d8ca9e39c78a4/cachetools-6.2.4-py3-none-any.whl", hash = "sha256:69a7a52634fed8b8bf6e24a050fb60bff1c9bd8f6d24572b99c32d4e71e62a51", size = 11551, upload-time = "2025-12-15T18:24:52.332Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -641,6 +641,7 @@ cx-oracle = [
|
|||
dev = [
|
||||
{ name = "coverage" },
|
||||
{ name = "dj-database-url" },
|
||||
{ name = "django-test-migrations" },
|
||||
{ name = "ipdb" },
|
||||
{ name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||
{ name = "ipython", version = "9.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
|
|
@ -661,7 +662,7 @@ docs = [
|
|||
{ name = "furo" },
|
||||
{ name = "readme-renderer", extra = ["md"] },
|
||||
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||
{ name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "sphinx-autobuild", version = "2024.10.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||
{ name = "sphinx-autobuild", version = "2025.8.25", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "sphinxcontrib-django" },
|
||||
|
|
@ -687,6 +688,7 @@ cx-oracle = [{ name = "cx-oracle", specifier = ">=8.3.0" }]
|
|||
dev = [
|
||||
{ name = "coverage", specifier = ">=7.6.1" },
|
||||
{ name = "dj-database-url", specifier = ">=2.2.0" },
|
||||
{ name = "django-test-migrations", specifier = ">=1.5.0" },
|
||||
{ name = "ipdb", specifier = ">=0.13.13" },
|
||||
{ name = "ipython", specifier = ">=8.18.1" },
|
||||
{ name = "mypy", specifier = ">=1.14.1" },
|
||||
|
|
@ -714,6 +716,18 @@ oracledb = [{ name = "oracledb", specifier = ">=2.3.0" }]
|
|||
psycopg2 = [{ name = "psycopg2", specifier = ">=2.9.10" }]
|
||||
psycopg3 = [{ name = "psycopg" }]
|
||||
|
||||
[[package]]
|
||||
name = "django-test-migrations"
|
||||
version = "1.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/be/ed/7fc6f8e89d83565fc4acb93ae0a2387d885ac83cda445cb6c570f302bf55/django_test_migrations-1.5.0.tar.gz", hash = "sha256:1cbff04b1e82c5564a6f635284907b381cc11a2ff883adff46776d9126824f07", size = 20143, upload-time = "2025-04-18T10:15:38.547Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/fe/38789c69f71adff9156bda7542d8fd05fcde1a109cf67bd7a1a139f8199f/django_test_migrations-1.5.0-py3-none-any.whl", hash = "sha256:96a08f085fc8bfaa53d44618341d82a2d22fd194c821cd81b147b66f0bec0da8", size = 25099, upload-time = "2025-04-18T10:15:37.16Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doc8"
|
||||
version = "2.0.0"
|
||||
|
|
@ -762,28 +776,28 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.20.0"
|
||||
version = "3.20.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a7/23/ce7a1126827cedeb958fc043d61745754464eb56c5937c35bbf2b8e26f34/filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c", size = 19476, upload-time = "2025-12-15T23:54:28.027Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/7f/a1a97644e39e7316d850784c642093c99df1290a460df4ede27659056834/filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a", size = 16666, upload-time = "2025-12-15T23:54:26.874Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "furo"
|
||||
version = "2025.9.25"
|
||||
version = "2025.12.19"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "accessible-pygments" },
|
||||
{ name = "beautifulsoup4" },
|
||||
{ name = "pygments" },
|
||||
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||
{ name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "sphinx-basic-ng" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4e/29/ff3b83a1ffce74676043ab3e7540d398e0b1ce7660917a00d7c4958b93da/furo-2025.9.25.tar.gz", hash = "sha256:3eac05582768fdbbc2bdfa1cdbcdd5d33cfc8b4bd2051729ff4e026a1d7e0a98", size = 1662007, upload-time = "2025-09-25T21:37:19.221Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ec/20/5f5ad4da6a5a27c80f2ed2ee9aee3f9e36c66e56e21c00fde467b2f8f88f/furo-2025.12.19.tar.gz", hash = "sha256:188d1f942037d8b37cd3985b955839fea62baa1730087dc29d157677c857e2a7", size = 1661473, upload-time = "2025-12-19T17:34:40.889Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/69/964b55f389c289e16ba2a5dfe587c3c462aac09e24123f09ddf703889584/furo-2025.9.25-py3-none-any.whl", hash = "sha256:2937f68e823b8e37b410c972c371bc2b1d88026709534927158e0cb3fac95afe", size = 340409, upload-time = "2025-09-25T21:37:17.244Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/b2/50e9b292b5cac13e9e81272c7171301abc753a60460d21505b606e15cf21/furo-2025.12.19-py3-none-any.whl", hash = "sha256:bb0ead5309f9500130665a26bee87693c41ce4dbdff864dbfb6b0dae4673d24f", size = 339262, upload-time = "2025-12-19T17:34:38.905Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -990,75 +1004,75 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "librt"
|
||||
version = "0.7.3"
|
||||
version = "0.7.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b3/d9/6f3d3fcf5e5543ed8a60cc70fa7d50508ed60b8a10e9af6d2058159ab54e/librt-0.7.3.tar.gz", hash = "sha256:3ec50cf65235ff5c02c5b747748d9222e564ad48597122a361269dd3aa808798", size = 144549, upload-time = "2025-12-06T19:04:45.553Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/93/e4/b59bdf1197fdf9888452ea4d2048cdad61aef85eb83e99dc52551d7fdc04/librt-0.7.4.tar.gz", hash = "sha256:3871af56c59864d5fd21d1ac001eb2fb3b140d52ba0454720f2e4a19812404ba", size = 145862, upload-time = "2025-12-15T16:52:43.862Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/66/79a14e672256ef58144a24eb49adb338ec02de67ff4b45320af6504682ab/librt-0.7.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2682162855a708e3270eba4b92026b93f8257c3e65278b456c77631faf0f4f7a", size = 54707, upload-time = "2025-12-06T19:03:10.881Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/fa/b709c65a9d5eab85f7bcfe0414504d9775aaad6e78727a0327e175474caa/librt-0.7.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:440c788f707c061d237c1e83edf6164ff19f5c0f823a3bf054e88804ebf971ec", size = 56670, upload-time = "2025-12-06T19:03:12.107Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/56/0685a0772ec89ddad4c00e6b584603274c3d818f9a68e2c43c4eb7b39ee9/librt-0.7.3-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399938edbd3d78339f797d685142dd8a623dfaded023cf451033c85955e4838a", size = 161045, upload-time = "2025-12-06T19:03:13.444Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/d9/863ada0c5ce48aefb89df1555e392b2209fcb6daee4c153c031339b9a89b/librt-0.7.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1975eda520957c6e0eb52d12968dd3609ffb7eef05d4223d097893d6daf1d8a7", size = 169532, upload-time = "2025-12-06T19:03:14.699Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/a0/71da6c8724fd16c31749905ef1c9e11de206d9301b5be984bf2682b4efb3/librt-0.7.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9da128d0edf990cf0d2ca011b02cd6f639e79286774bd5b0351245cbb5a6e51", size = 183277, upload-time = "2025-12-06T19:03:16.446Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/bf/9c97bf2f8338ba1914de233ea312bba2bbd7c59f43f807b3e119796bab18/librt-0.7.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19acfde38cb532a560b98f473adc741c941b7a9bc90f7294bc273d08becb58b", size = 179045, upload-time = "2025-12-06T19:03:17.838Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/b1/ceea067f489e904cb4ddcca3c9b06ba20229bc3fa7458711e24a5811f162/librt-0.7.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7b4f57f7a0c65821c5441d98c47ff7c01d359b1e12328219709bdd97fdd37f90", size = 173521, upload-time = "2025-12-06T19:03:19.17Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/41/6cb18f5da9c89ed087417abb0127a445a50ad4eaf1282ba5b52588187f47/librt-0.7.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:256793988bff98040de23c57cf36e1f4c2f2dc3dcd17537cdac031d3b681db71", size = 193592, upload-time = "2025-12-06T19:03:20.637Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/3c/fcef208746584e7c78584b7aedc617130c4a4742cb8273361bbda8b183b5/librt-0.7.3-cp310-cp310-win32.whl", hash = "sha256:fcb72249ac4ea81a7baefcbff74df7029c3cb1cf01a711113fa052d563639c9c", size = 47201, upload-time = "2025-12-06T19:03:21.764Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/bf/d8a6c35d1b2b789a4df9b3ddb1c8f535ea373fde2089698965a8f0d62138/librt-0.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:4887c29cadbdc50640179e3861c276325ff2986791e6044f73136e6e798ff806", size = 54371, upload-time = "2025-12-06T19:03:23.231Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/e6/f6391f5c6f158d31ed9af6bd1b1bcd3ffafdea1d816bc4219d0d90175a7f/librt-0.7.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:687403cced6a29590e6be6964463835315905221d797bc5c934a98750fe1a9af", size = 54711, upload-time = "2025-12-06T19:03:24.6Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/1b/53c208188c178987c081560a0fcf36f5ca500d5e21769596c845ef2f40d4/librt-0.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:24d70810f6e2ea853ff79338001533716b373cc0f63e2a0be5bc96129edb5fb5", size = 56664, upload-time = "2025-12-06T19:03:25.969Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/5c/d9da832b9a1e5f8366e8a044ec80217945385b26cb89fd6f94bfdc7d80b0/librt-0.7.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bf8c7735fbfc0754111f00edda35cf9e98a8d478de6c47b04eaa9cef4300eaa7", size = 161701, upload-time = "2025-12-06T19:03:27.035Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/aa/1e0a7aba15e78529dd21f233076b876ee58c8b8711b1793315bdd3b263b0/librt-0.7.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32d43610dff472eab939f4d7fbdd240d1667794192690433672ae22d7af8445", size = 171040, upload-time = "2025-12-06T19:03:28.482Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/46/3cfa325c1c2bc25775ec6ec1718cfbec9cff4ac767d37d2d3a2d1cc6f02c/librt-0.7.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:adeaa886d607fb02563c1f625cf2ee58778a2567c0c109378da8f17ec3076ad7", size = 184720, upload-time = "2025-12-06T19:03:29.599Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/bb/e4553433d7ac47f4c75d0a7e59b13aee0e08e88ceadbee356527a9629b0a/librt-0.7.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:572a24fc5958c61431da456a0ef1eeea6b4989d81eeb18b8e5f1f3077592200b", size = 180731, upload-time = "2025-12-06T19:03:31.201Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/89/51cd73006232981a3106d4081fbaa584ac4e27b49bc02266468d3919db03/librt-0.7.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6488e69d408b492e08bfb68f20c4a899a354b4386a446ecd490baff8d0862720", size = 174565, upload-time = "2025-12-06T19:03:32.818Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/54/0578a78b587e5aa22486af34239a052c6366835b55fc307bc64380229e3f/librt-0.7.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ed028fc3d41adda916320712838aec289956c89b4f0a361ceadf83a53b4c047a", size = 195247, upload-time = "2025-12-06T19:03:34.434Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/0a/ee747cd999753dd9447e50b98fc36ee433b6c841a42dbf6d47b64b32a56e/librt-0.7.3-cp311-cp311-win32.whl", hash = "sha256:2cf9d73499486ce39eebbff5f42452518cc1f88d8b7ea4a711ab32962b176ee2", size = 47514, upload-time = "2025-12-06T19:03:35.959Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/af/8b13845178dec488e752878f8e290f8f89e7e34ae1528b70277aa1a6dd1e/librt-0.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:35f1609e3484a649bb80431310ddbec81114cd86648f1d9482bc72a3b86ded2e", size = 54695, upload-time = "2025-12-06T19:03:36.956Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/7a/ae59578501b1a25850266778f59279f4f3e726acc5c44255bfcb07b4bc57/librt-0.7.3-cp311-cp311-win_arm64.whl", hash = "sha256:550fdbfbf5bba6a2960b27376ca76d6aaa2bd4b1a06c4255edd8520c306fcfc0", size = 48142, upload-time = "2025-12-06T19:03:38.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/90/ed8595fa4e35b6020317b5ea8d226a782dcbac7a997c19ae89fb07a41c66/librt-0.7.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0fa9ac2e49a6bee56e47573a6786cb635e128a7b12a0dc7851090037c0d397a3", size = 55687, upload-time = "2025-12-06T19:03:39.245Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/f6/6a20702a07b41006cb001a759440cb6b5362530920978f64a2b2ae2bf729/librt-0.7.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e980cf1ed1a2420a6424e2ed884629cdead291686f1048810a817de07b5eb18", size = 57127, upload-time = "2025-12-06T19:03:40.3Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/f3/b0c4703d5ffe9359b67bb2ccb86c42d4e930a363cfc72262ac3ba53cff3e/librt-0.7.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e094e445c37c57e9ec612847812c301840239d34ccc5d153a982fa9814478c60", size = 165336, upload-time = "2025-12-06T19:03:41.369Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/69/3ba05b73ab29ccbe003856232cea4049769be5942d799e628d1470ed1694/librt-0.7.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aca73d70c3f553552ba9133d4a09e767dcfeee352d8d8d3eb3f77e38a3beb3ed", size = 174237, upload-time = "2025-12-06T19:03:42.44Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/ad/d7c2671e7bf6c285ef408aa435e9cd3fdc06fd994601e1f2b242df12034f/librt-0.7.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c634a0a6db395fdaba0361aa78395597ee72c3aad651b9a307a3a7eaf5efd67e", size = 189017, upload-time = "2025-12-06T19:03:44.01Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/94/d13f57193148004592b618555f296b41d2d79b1dc814ff8b3273a0bf1546/librt-0.7.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a59a69deeb458c858b8fea6acf9e2acd5d755d76cd81a655256bc65c20dfff5b", size = 183983, upload-time = "2025-12-06T19:03:45.834Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/10/b612a9944ebd39fa143c7e2e2d33f2cb790205e025ddd903fb509a3a3bb3/librt-0.7.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d91e60ac44bbe3a77a67af4a4c13114cbe9f6d540337ce22f2c9eaf7454ca71f", size = 177602, upload-time = "2025-12-06T19:03:46.944Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/48/77bc05c4cc232efae6c5592c0095034390992edbd5bae8d6cf1263bb7157/librt-0.7.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:703456146dc2bf430f7832fd1341adac5c893ec3c1430194fdcefba00012555c", size = 199282, upload-time = "2025-12-06T19:03:48.069Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/aa/05916ccd864227db1ffec2a303ae34f385c6b22d4e7ce9f07054dbcf083c/librt-0.7.3-cp312-cp312-win32.whl", hash = "sha256:b7c1239b64b70be7759554ad1a86288220bbb04d68518b527783c4ad3fb4f80b", size = 47879, upload-time = "2025-12-06T19:03:49.289Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/92/7f41c42d31ea818b3c4b9cc1562e9714bac3c676dd18f6d5dd3d0f2aa179/librt-0.7.3-cp312-cp312-win_amd64.whl", hash = "sha256:ef59c938f72bdbc6ab52dc50f81d0637fde0f194b02d636987cea2ab30f8f55a", size = 54972, upload-time = "2025-12-06T19:03:50.335Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/dc/53582bbfb422311afcbc92adb75711f04e989cec052f08ec0152fbc36c9c/librt-0.7.3-cp312-cp312-win_arm64.whl", hash = "sha256:ff21c554304e8226bf80c3a7754be27c6c3549a9fec563a03c06ee8f494da8fc", size = 48338, upload-time = "2025-12-06T19:03:51.431Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/7d/e0ce1837dfb452427db556e6d4c5301ba3b22fe8de318379fbd0593759b9/librt-0.7.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56f2a47beda8409061bc1c865bef2d4bd9ff9255219402c0817e68ab5ad89aed", size = 55742, upload-time = "2025-12-06T19:03:52.459Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/c0/3564262301e507e1d5cf31c7d84cb12addf0d35e05ba53312494a2eba9a4/librt-0.7.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:14569ac5dd38cfccf0a14597a88038fb16811a6fede25c67b79c6d50fc2c8fdc", size = 57163, upload-time = "2025-12-06T19:03:53.516Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/ac/245e72b7e443d24a562f6047563c7f59833384053073ef9410476f68505b/librt-0.7.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6038ccbd5968325a5d6fd393cf6e00b622a8de545f0994b89dd0f748dcf3e19e", size = 165840, upload-time = "2025-12-06T19:03:54.918Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/af/587e4491f40adba066ba39a450c66bad794c8d92094f936a201bfc7c2b5f/librt-0.7.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d39079379a9a28e74f4d57dc6357fa310a1977b51ff12239d7271ec7e71d67f5", size = 174827, upload-time = "2025-12-06T19:03:56.082Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/21/5b8c60ea208bc83dd00421022a3874330685d7e856404128dc3728d5d1af/librt-0.7.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8837d5a52a2d7aa9f4c3220a8484013aed1d8ad75240d9a75ede63709ef89055", size = 189612, upload-time = "2025-12-06T19:03:57.507Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/2f/8b819169ef696421fb81cd04c6cdf225f6e96f197366001e9d45180d7e9e/librt-0.7.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:399bbd7bcc1633c3e356ae274a1deb8781c7bf84d9c7962cc1ae0c6e87837292", size = 184584, upload-time = "2025-12-06T19:03:58.686Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/fc/af9d225a9395b77bd7678362cb055d0b8139c2018c37665de110ca388022/librt-0.7.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8d8cf653e798ee4c4e654062b633db36984a1572f68c3aa25e364a0ddfbbb910", size = 178269, upload-time = "2025-12-06T19:03:59.769Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/d8/7b4fa1683b772966749d5683aa3fd605813defffe157833a8fa69cc89207/librt-0.7.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2f03484b54bf4ae80ab2e504a8d99d20d551bfe64a7ec91e218010b467d77093", size = 199852, upload-time = "2025-12-06T19:04:00.901Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/e8/4598413aece46ca38d9260ef6c51534bd5f34b5c21474fcf210ce3a02123/librt-0.7.3-cp313-cp313-win32.whl", hash = "sha256:44b3689b040df57f492e02cd4f0bacd1b42c5400e4b8048160c9d5e866de8abe", size = 47936, upload-time = "2025-12-06T19:04:02.054Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/80/ac0e92d5ef8c6791b3e2c62373863827a279265e0935acdf807901353b0e/librt-0.7.3-cp313-cp313-win_amd64.whl", hash = "sha256:6b407c23f16ccc36614c136251d6b32bf30de7a57f8e782378f1107be008ddb0", size = 54965, upload-time = "2025-12-06T19:04:03.224Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/fd/042f823fcbff25c1449bb4203a29919891ca74141b68d3a5f6612c4ce283/librt-0.7.3-cp313-cp313-win_arm64.whl", hash = "sha256:abfc57cab3c53c4546aee31859ef06753bfc136c9d208129bad23e2eca39155a", size = 48350, upload-time = "2025-12-06T19:04:04.234Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/ae/c6ecc7bb97134a71b5241e8855d39964c0e5f4d96558f0d60593892806d2/librt-0.7.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:120dd21d46ff875e849f1aae19346223cf15656be489242fe884036b23d39e93", size = 55175, upload-time = "2025-12-06T19:04:05.308Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/bc/2cc0cb0ab787b39aa5c7645cd792433c875982bdf12dccca558b89624594/librt-0.7.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1617bea5ab31266e152871208502ee943cb349c224846928a1173c864261375e", size = 56881, upload-time = "2025-12-06T19:04:06.674Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/87/397417a386190b70f5bf26fcedbaa1515f19dce33366e2684c6b7ee83086/librt-0.7.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:93b2a1f325fefa1482516ced160c8c7b4b8d53226763fa6c93d151fa25164207", size = 163710, upload-time = "2025-12-06T19:04:08.437Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/37/7338f85b80e8a17525d941211451199845093ca242b32efbf01df8531e72/librt-0.7.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d4801db8354436fd3936531e7f0e4feb411f62433a6b6cb32bb416e20b529f", size = 172471, upload-time = "2025-12-06T19:04:10.124Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/e0/741704edabbfae2c852fedc1b40d9ed5a783c70ed3ed8e4fe98f84b25d13/librt-0.7.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11ad45122bbed42cfc8b0597450660126ef28fd2d9ae1a219bc5af8406f95678", size = 186804, upload-time = "2025-12-06T19:04:11.586Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/d1/0a82129d6ba242f3be9af34815be089f35051bc79619f5c27d2c449ecef6/librt-0.7.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6b4e7bff1d76dd2b46443078519dc75df1b5e01562345f0bb740cea5266d8218", size = 181817, upload-time = "2025-12-06T19:04:12.802Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/32/704f80bcf9979c68d4357c46f2af788fbf9d5edda9e7de5786ed2255e911/librt-0.7.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:d86f94743a11873317094326456b23f8a5788bad9161fd2f0e52088c33564620", size = 175602, upload-time = "2025-12-06T19:04:14.004Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/6d/4355cfa0fae0c062ba72f541d13db5bc575770125a7ad3d4f46f4109d305/librt-0.7.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:754a0d09997095ad764ccef050dd5bf26cbf457aab9effcba5890dad081d879e", size = 196497, upload-time = "2025-12-06T19:04:15.487Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/eb/ac6d8517d44209e5a712fde46f26d0055e3e8969f24d715f70bd36056230/librt-0.7.3-cp314-cp314-win32.whl", hash = "sha256:fbd7351d43b80d9c64c3cfcb50008f786cc82cba0450e8599fdd64f264320bd3", size = 44678, upload-time = "2025-12-06T19:04:16.688Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/93/238f026d141faf9958da588c761a0812a1a21c98cc54a76f3608454e4e59/librt-0.7.3-cp314-cp314-win_amd64.whl", hash = "sha256:d376a35c6561e81d2590506804b428fc1075fcc6298fc5bb49b771534c0ba010", size = 51689, upload-time = "2025-12-06T19:04:17.726Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/44/43f462ad9dcf9ed7d3172fe2e30d77b980956250bd90e9889a9cca93df2a/librt-0.7.3-cp314-cp314-win_arm64.whl", hash = "sha256:cbdb3f337c88b43c3b49ca377731912c101178be91cb5071aac48faa898e6f8e", size = 44662, upload-time = "2025-12-06T19:04:18.771Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/35/fed6348915f96b7323241de97f26e2af481e95183b34991df12fd5ce31b1/librt-0.7.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9f0e0927efe87cd42ad600628e595a1a0aa1c64f6d0b55f7e6059079a428641a", size = 57347, upload-time = "2025-12-06T19:04:19.812Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/f2/045383ccc83e3fea4fba1b761796584bc26817b6b2efb6b8a6731431d16f/librt-0.7.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:020c6db391268bcc8ce75105cb572df8cb659a43fd347366aaa407c366e5117a", size = 59223, upload-time = "2025-12-06T19:04:20.862Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/3f/c081f8455ab1d7f4a10dbe58463ff97119272ff32494f21839c3b9029c2c/librt-0.7.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7af7785f5edd1f418da09a8cdb9ec84b0213e23d597413e06525340bcce1ea4f", size = 183861, upload-time = "2025-12-06T19:04:21.963Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/f5/73c5093c22c31fbeaebc25168837f05ebfd8bf26ce00855ef97a5308f36f/librt-0.7.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ccadf260bb46a61b9c7e89e2218f6efea9f3eeaaab4e3d1f58571890e54858e", size = 194594, upload-time = "2025-12-06T19:04:23.14Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/b8/d5f17d4afe16612a4a94abfded94c16c5a033f183074fb130dfe56fc1a42/librt-0.7.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9883b2d819ce83f87ba82a746c81d14ada78784db431e57cc9719179847376e", size = 206759, upload-time = "2025-12-06T19:04:24.328Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/2e/021765c1be85ee23ffd5b5b968bb4cba7526a4db2a0fc27dcafbdfc32da7/librt-0.7.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:59cb0470612d21fa1efddfa0dd710756b50d9c7fb6c1236bbf8ef8529331dc70", size = 203210, upload-time = "2025-12-06T19:04:25.544Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/f0/9923656e42da4fd18c594bd08cf6d7e152d4158f8b808e210d967f0dcceb/librt-0.7.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:1fe603877e1865b5fd047a5e40379509a4a60204aa7aa0f72b16f7a41c3f0712", size = 196708, upload-time = "2025-12-06T19:04:26.725Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/0b/0708b886ac760e64d6fbe7e16024e4be3ad1a3629d19489a97e9cf4c3431/librt-0.7.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5460d99ed30f043595bbdc888f542bad2caeb6226b01c33cda3ae444e8f82d42", size = 217212, upload-time = "2025-12-06T19:04:27.892Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/7f/12a73ff17bca4351e73d585dd9ebf46723c4a8622c4af7fe11a2e2d011ff/librt-0.7.3-cp314-cp314t-win32.whl", hash = "sha256:d09f677693328503c9e492e33e9601464297c01f9ebd966ea8fc5308f3069bfd", size = 45586, upload-time = "2025-12-06T19:04:29.116Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/df/8decd032ac9b995e4f5606cde783711a71094128d88d97a52e397daf2c89/librt-0.7.3-cp314-cp314t-win_amd64.whl", hash = "sha256:25711f364c64cab2c910a0247e90b51421e45dbc8910ceeb4eac97a9e132fc6f", size = 53002, upload-time = "2025-12-06T19:04:30.173Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/0c/6605b6199de8178afe7efc77ca1d8e6db00453bc1d3349d27605c0f42104/librt-0.7.3-cp314-cp314t-win_arm64.whl", hash = "sha256:a9f9b661f82693eb56beb0605156c7fca57f535704ab91837405913417d6990b", size = 45647, upload-time = "2025-12-06T19:04:31.302Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/1e/3e61dff6c07a3b400fe907d3164b92b3b3023ef86eac1ee236869dc276f7/librt-0.7.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dc300cb5a5a01947b1ee8099233156fdccd5001739e5f596ecfbc0dab07b5a3b", size = 54708, upload-time = "2025-12-15T16:51:03.752Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/98/ab2428b0a80d0fd67decaeea84a5ec920e3dd4d95ecfd074c71f51bd7315/librt-0.7.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee8d3323d921e0f6919918a97f9b5445a7dfe647270b2629ec1008aa676c0bc0", size = 56656, upload-time = "2025-12-15T16:51:05.038Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/ce/de1fad3a16e4fb5b6605bd6cbe6d0e5207cc8eca58993835749a1da0812b/librt-0.7.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:95cb80854a355b284c55f79674f6187cc9574df4dc362524e0cce98c89ee8331", size = 161024, upload-time = "2025-12-15T16:51:06.31Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/00/ddfcdc1147dd7fb68321d7b064b12f0b9101d85f466a46006f86096fde8d/librt-0.7.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ca1caedf8331d8ad6027f93b52d68ed8f8009f5c420c246a46fe9d3be06be0f", size = 169529, upload-time = "2025-12-15T16:51:07.907Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/b3/915702c7077df2483b015030d1979404474f490fe9a071e9576f7b26fef6/librt-0.7.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2a6f1236151e6fe1da289351b5b5bce49651c91554ecc7b70a947bced6fe212", size = 183270, upload-time = "2025-12-15T16:51:09.164Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/19/ab2f217e8ec509fca4ea9e2e5022b9f72c1a7b7195f5a5770d299df807ea/librt-0.7.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7766b57aeebaf3f1dac14fdd4a75c9a61f2ed56d8ebeefe4189db1cb9d2a3783", size = 179038, upload-time = "2025-12-15T16:51:10.538Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/1c/d40851d187662cf50312ebbc0b277c7478dd78dbaaf5ee94056f1d7f2f83/librt-0.7.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1c4c89fb01157dd0a3bfe9e75cd6253b0a1678922befcd664eca0772a4c6c979", size = 173502, upload-time = "2025-12-15T16:51:11.888Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/52/d5880835c772b22c38db18660420fa6901fd9e9a433b65f0ba9b0f4da764/librt-0.7.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f7fa8beef580091c02b4fd26542de046b2abfe0aaefa02e8bcf68acb7618f2b3", size = 193570, upload-time = "2025-12-15T16:51:13.168Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/35/22d3c424b82f86ce019c0addadf001d459dfac8036aecc07fadc5c541053/librt-0.7.4-cp310-cp310-win32.whl", hash = "sha256:543c42fa242faae0466fe72d297976f3c710a357a219b1efde3a0539a68a6997", size = 42596, upload-time = "2025-12-15T16:51:14.422Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/b1/e7c316ac5fe60ac1fdfe515198087205220803c4cf923ee63e1cb8380b17/librt-0.7.4-cp310-cp310-win_amd64.whl", hash = "sha256:25cc40d8eb63f0a7ea4c8f49f524989b9df901969cb860a2bc0e4bad4b8cb8a8", size = 48972, upload-time = "2025-12-15T16:51:15.516Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/64/44089b12d8b4714a7f0e2f33fb19285ba87702d4be0829f20b36ebeeee07/librt-0.7.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3485b9bb7dfa66167d5500ffdafdc35415b45f0da06c75eb7df131f3357b174a", size = 54709, upload-time = "2025-12-15T16:51:16.699Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/ef/6fa39fb5f37002f7d25e0da4f24d41b457582beea9369eeb7e9e73db5508/librt-0.7.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:188b4b1a770f7f95ea035d5bbb9d7367248fc9d12321deef78a269ebf46a5729", size = 56663, upload-time = "2025-12-15T16:51:17.856Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/e4/cbaca170a13bee2469c90df9e47108610b4422c453aea1aec1779ac36c24/librt-0.7.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1b668b1c840183e4e38ed5a99f62fac44c3a3eef16870f7f17cfdfb8b47550ed", size = 161703, upload-time = "2025-12-15T16:51:19.421Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/32/0b2296f9cc7e693ab0d0835e355863512e5eac90450c412777bd699c76ae/librt-0.7.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0e8f864b521f6cfedb314d171630f827efee08f5c3462bcbc2244ab8e1768cd6", size = 171027, upload-time = "2025-12-15T16:51:20.721Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/33/c70b6d40f7342716e5f1353c8da92d9e32708a18cbfa44897a93ec2bf879/librt-0.7.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df7c9def4fc619a9c2ab402d73a0c5b53899abe090e0100323b13ccb5a3dd82", size = 184700, upload-time = "2025-12-15T16:51:22.272Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/c8/555c405155da210e4c4113a879d378f54f850dbc7b794e847750a8fadd43/librt-0.7.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f79bc3595b6ed159a1bf0cdc70ed6ebec393a874565cab7088a219cca14da727", size = 180719, upload-time = "2025-12-15T16:51:23.561Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/88/34dc1f1461c5613d1b73f0ecafc5316cc50adcc1b334435985b752ed53e5/librt-0.7.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77772a4b8b5f77d47d883846928c36d730b6e612a6388c74cba33ad9eb149c11", size = 174535, upload-time = "2025-12-15T16:51:25.031Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/5a/f3fafe80a221626bcedfa9fe5abbf5f04070989d44782f579b2d5920d6d0/librt-0.7.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:064a286e6ab0b4c900e228ab4fa9cb3811b4b83d3e0cc5cd816b2d0f548cb61c", size = 195236, upload-time = "2025-12-15T16:51:26.328Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/77/5c048d471ce17f4c3a6e08419be19add4d291e2f7067b877437d482622ac/librt-0.7.4-cp311-cp311-win32.whl", hash = "sha256:42da201c47c77b6cc91fc17e0e2b330154428d35d6024f3278aa2683e7e2daf2", size = 42930, upload-time = "2025-12-15T16:51:27.853Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/3b/514a86305a12c3d9eac03e424b07cd312c7343a9f8a52719aa079590a552/librt-0.7.4-cp311-cp311-win_amd64.whl", hash = "sha256:d31acb5886c16ae1711741f22504195af46edec8315fe69b77e477682a87a83e", size = 49240, upload-time = "2025-12-15T16:51:29.037Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/01/3b7b1914f565926b780a734fac6e9a4d2c7aefe41f4e89357d73697a9457/librt-0.7.4-cp311-cp311-win_arm64.whl", hash = "sha256:114722f35093da080a333b3834fff04ef43147577ed99dd4db574b03a5f7d170", size = 42613, upload-time = "2025-12-15T16:51:30.194Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/e7/b805d868d21f425b7e76a0ea71a2700290f2266a4f3c8357fcf73efc36aa/librt-0.7.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7dd3b5c37e0fb6666c27cf4e2c88ae43da904f2155c4cfc1e5a2fdce3b9fcf92", size = 55688, upload-time = "2025-12-15T16:51:31.571Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/5e/69a2b02e62a14cfd5bfd9f1e9adea294d5bcfeea219c7555730e5d068ee4/librt-0.7.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9c5de1928c486201b23ed0cc4ac92e6e07be5cd7f3abc57c88a9cf4f0f32108", size = 57141, upload-time = "2025-12-15T16:51:32.714Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/6b/05dba608aae1272b8ea5ff8ef12c47a4a099a04d1e00e28a94687261d403/librt-0.7.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:078ae52ffb3f036396cc4aed558e5b61faedd504a3c1f62b8ae34bf95ae39d94", size = 165322, upload-time = "2025-12-15T16:51:33.986Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/bc/199533d3fc04a4cda8d7776ee0d79955ab0c64c79ca079366fbc2617e680/librt-0.7.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce58420e25097b2fc201aef9b9f6d65df1eb8438e51154e1a7feb8847e4a55ab", size = 174216, upload-time = "2025-12-15T16:51:35.384Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/ec/09239b912a45a8ed117cb4a6616d9ff508f5d3131bd84329bf2f8d6564f1/librt-0.7.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b719c8730c02a606dc0e8413287e8e94ac2d32a51153b300baf1f62347858fba", size = 189005, upload-time = "2025-12-15T16:51:36.687Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/2e/e188313d54c02f5b0580dd31476bb4b0177514ff8d2be9f58d4a6dc3a7ba/librt-0.7.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3749ef74c170809e6dee68addec9d2458700a8de703de081c888e92a8b015cf9", size = 183960, upload-time = "2025-12-15T16:51:37.977Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/84/f1d568d254518463d879161d3737b784137d236075215e56c7c9be191cee/librt-0.7.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b35c63f557653c05b5b1b6559a074dbabe0afee28ee2a05b6c9ba21ad0d16a74", size = 177609, upload-time = "2025-12-15T16:51:40.584Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/43/060bbc1c002f0d757c33a1afe6bf6a565f947a04841139508fc7cef6c08b/librt-0.7.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1ef704e01cb6ad39ad7af668d51677557ca7e5d377663286f0ee1b6b27c28e5f", size = 199269, upload-time = "2025-12-15T16:51:41.879Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/7f/708f8f02d8012ee9f366c07ea6a92882f48bd06cc1ff16a35e13d0fbfb08/librt-0.7.4-cp312-cp312-win32.whl", hash = "sha256:c66c2b245926ec15188aead25d395091cb5c9df008d3b3207268cd65557d6286", size = 43186, upload-time = "2025-12-15T16:51:43.149Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/a5/4e051b061c8b2509be31b2c7ad4682090502c0a8b6406edcf8c6b4fe1ef7/librt-0.7.4-cp312-cp312-win_amd64.whl", hash = "sha256:71a56f4671f7ff723451f26a6131754d7c1809e04e22ebfbac1db8c9e6767a20", size = 49455, upload-time = "2025-12-15T16:51:44.336Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/d2/90d84e9f919224a3c1f393af1636d8638f54925fdc6cd5ee47f1548461e5/librt-0.7.4-cp312-cp312-win_arm64.whl", hash = "sha256:419eea245e7ec0fe664eb7e85e7ff97dcdb2513ca4f6b45a8ec4a3346904f95a", size = 42828, upload-time = "2025-12-15T16:51:45.498Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/4d/46a53ccfbb39fd0b493fd4496eb76f3ebc15bb3e45d8c2e695a27587edf5/librt-0.7.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d44a1b1ba44cbd2fc3cb77992bef6d6fdb1028849824e1dd5e4d746e1f7f7f0b", size = 55745, upload-time = "2025-12-15T16:51:46.636Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/2b/3ac7f5212b1828bf4f979cf87f547db948d3e28421d7a430d4db23346ce4/librt-0.7.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c9cab4b3de1f55e6c30a84c8cee20e4d3b2476f4d547256694a1b0163da4fe32", size = 57166, upload-time = "2025-12-15T16:51:48.219Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/99/6523509097cbe25f363795f0c0d1c6a3746e30c2994e25b5aefdab119b21/librt-0.7.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2857c875f1edd1feef3c371fbf830a61b632fb4d1e57160bb1e6a3206e6abe67", size = 165833, upload-time = "2025-12-15T16:51:49.443Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/35/323611e59f8fe032649b4fb7e77f746f96eb7588fcbb31af26bae9630571/librt-0.7.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b370a77be0a16e1ad0270822c12c21462dc40496e891d3b0caf1617c8cc57e20", size = 174818, upload-time = "2025-12-15T16:51:51.015Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/e6/40fb2bb21616c6e06b6a64022802228066e9a31618f493e03f6b9661548a/librt-0.7.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d05acd46b9a52087bfc50c59dfdf96a2c480a601e8898a44821c7fd676598f74", size = 189607, upload-time = "2025-12-15T16:51:52.671Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/48/1b47c7d5d28b775941e739ed2bfe564b091c49201b9503514d69e4ed96d7/librt-0.7.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:70969229cb23d9c1a80e14225838d56e464dc71fa34c8342c954fc50e7516dee", size = 184585, upload-time = "2025-12-15T16:51:54.027Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/a6/ee135dfb5d3b54d5d9001dbe483806229c6beac3ee2ba1092582b7efeb1b/librt-0.7.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4450c354b89dbb266730893862dbff06006c9ed5b06b6016d529b2bf644fc681", size = 178249, upload-time = "2025-12-15T16:51:55.248Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/87/d5b84ec997338be26af982bcd6679be0c1db9a32faadab1cf4bb24f9e992/librt-0.7.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:adefe0d48ad35b90b6f361f6ff5a1bd95af80c17d18619c093c60a20e7a5b60c", size = 199851, upload-time = "2025-12-15T16:51:56.933Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/63/ba1333bf48306fe398e3392a7427ce527f81b0b79d0d91618c4610ce9d15/librt-0.7.4-cp313-cp313-win32.whl", hash = "sha256:21ea710e96c1e050635700695095962a22ea420d4b3755a25e4909f2172b4ff2", size = 43249, upload-time = "2025-12-15T16:51:58.498Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/8a/de2c6df06cdfa9308c080e6b060fe192790b6a48a47320b215e860f0e98c/librt-0.7.4-cp313-cp313-win_amd64.whl", hash = "sha256:772e18696cf5a64afee908662fbcb1f907460ddc851336ee3a848ef7684c8e1e", size = 49417, upload-time = "2025-12-15T16:51:59.618Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/66/8ee0949efc389691381ed686185e43536c20e7ad880c122dd1f31e65c658/librt-0.7.4-cp313-cp313-win_arm64.whl", hash = "sha256:52e34c6af84e12921748c8354aa6acf1912ca98ba60cdaa6920e34793f1a0788", size = 42824, upload-time = "2025-12-15T16:52:00.784Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/81/6921e65c8708eb6636bbf383aa77e6c7dad33a598ed3b50c313306a2da9d/librt-0.7.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4f1ee004942eaaed6e06c087d93ebc1c67e9a293e5f6b9b5da558df6bf23dc5d", size = 55191, upload-time = "2025-12-15T16:52:01.97Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/d6/3eb864af8a8de8b39cc8dd2e9ded1823979a27795d72c4eea0afa8c26c9f/librt-0.7.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d854c6dc0f689bad7ed452d2a3ecff58029d80612d336a45b62c35e917f42d23", size = 56898, upload-time = "2025-12-15T16:52:03.356Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/bc/b1d4c0711fdf79646225d576faee8747b8528a6ec1ceb6accfd89ade7102/librt-0.7.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a4f7339d9e445280f23d63dea842c0c77379c4a47471c538fc8feedab9d8d063", size = 163725, upload-time = "2025-12-15T16:52:04.572Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/08/61c41cd8f0a6a41fc99ea78a2205b88187e45ba9800792410ed62f033584/librt-0.7.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39003fc73f925e684f8521b2dbf34f61a5deb8a20a15dcf53e0d823190ce8848", size = 172469, upload-time = "2025-12-15T16:52:05.863Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/c7/4ee18b4d57f01444230bc18cf59103aeab8f8c0f45e84e0e540094df1df1/librt-0.7.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bb15ee29d95875ad697d449fe6071b67f730f15a6961913a2b0205015ca0843", size = 186804, upload-time = "2025-12-15T16:52:07.192Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/af/009e8ba3fbf830c936842da048eda1b34b99329f402e49d88fafff6525d1/librt-0.7.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:02a69369862099e37d00765583052a99d6a68af7e19b887e1b78fee0146b755a", size = 181807, upload-time = "2025-12-15T16:52:08.554Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/26/51ae25f813656a8b117c27a974f25e8c1e90abcd5a791ac685bf5b489a1b/librt-0.7.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ec72342cc4d62f38b25a94e28b9efefce41839aecdecf5e9627473ed04b7be16", size = 175595, upload-time = "2025-12-15T16:52:10.186Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/93/36d6c71f830305f88996b15c8e017aa8d1e03e2e947b40b55bbf1a34cf24/librt-0.7.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:776dbb9bfa0fc5ce64234b446995d8d9f04badf64f544ca036bd6cff6f0732ce", size = 196504, upload-time = "2025-12-15T16:52:11.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/11/8299e70862bb9d704735bf132c6be09c17b00fbc7cda0429a9df222fdc1b/librt-0.7.4-cp314-cp314-win32.whl", hash = "sha256:0f8cac84196d0ffcadf8469d9ded4d4e3a8b1c666095c2a291e22bf58e1e8a9f", size = 39738, upload-time = "2025-12-15T16:52:12.962Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/d5/656b0126e4e0f8e2725cd2d2a1ec40f71f37f6f03f135a26b663c0e1a737/librt-0.7.4-cp314-cp314-win_amd64.whl", hash = "sha256:037f5cb6fe5abe23f1dc058054d50e9699fcc90d0677eee4e4f74a8677636a1a", size = 45976, upload-time = "2025-12-15T16:52:14.441Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/86/465ff07b75c1067da8fa7f02913c4ead096ef106cfac97a977f763783bfb/librt-0.7.4-cp314-cp314-win_arm64.whl", hash = "sha256:a5deebb53d7a4d7e2e758a96befcd8edaaca0633ae71857995a0f16033289e44", size = 39073, upload-time = "2025-12-15T16:52:15.621Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/a0/24941f85960774a80d4b3c2aec651d7d980466da8101cae89e8b032a3e21/librt-0.7.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b4c25312c7f4e6ab35ab16211bdf819e6e4eddcba3b2ea632fb51c9a2a97e105", size = 57369, upload-time = "2025-12-15T16:52:16.782Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/a0/ddb259cae86ab415786c1547d0fe1b40f04a7b089f564fd5c0242a3fafb2/librt-0.7.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:618b7459bb392bdf373f2327e477597fff8f9e6a1878fffc1b711c013d1b0da4", size = 59230, upload-time = "2025-12-15T16:52:18.259Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/11/77823cb530ab8a0c6fac848ac65b745be446f6f301753b8990e8809080c9/librt-0.7.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1437c3f72a30c7047f16fd3e972ea58b90172c3c6ca309645c1c68984f05526a", size = 183869, upload-time = "2025-12-15T16:52:19.457Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/ce/157db3614cf3034b3f702ae5ba4fefda4686f11eea4b7b96542324a7a0e7/librt-0.7.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c96cb76f055b33308f6858b9b594618f1b46e147a4d03a4d7f0c449e304b9b95", size = 194606, upload-time = "2025-12-15T16:52:20.795Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/ef/6ec4c7e3d6490f69a4fd2803516fa5334a848a4173eac26d8ee6507bff6e/librt-0.7.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28f990e6821204f516d09dc39966ef8b84556ffd648d5926c9a3f681e8de8906", size = 206776, upload-time = "2025-12-15T16:52:22.229Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/22/750b37bf549f60a4782ab80e9d1e9c44981374ab79a7ea68670159905918/librt-0.7.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc4aebecc79781a1b77d7d4e7d9fe080385a439e198d993b557b60f9117addaf", size = 203205, upload-time = "2025-12-15T16:52:23.603Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/87/2e8a0f584412a93df5faad46c5fa0a6825fdb5eba2ce482074b114877f44/librt-0.7.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:022cc673e69283a42621dd453e2407cf1647e77f8bd857d7ad7499901e62376f", size = 196696, upload-time = "2025-12-15T16:52:24.951Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/ca/7bf78fa950e43b564b7de52ceeb477fb211a11f5733227efa1591d05a307/librt-0.7.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2b3ca211ae8ea540569e9c513da052699b7b06928dcda61247cb4f318122bdb5", size = 217191, upload-time = "2025-12-15T16:52:26.194Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/49/3732b0e8424ae35ad5c3166d9dd5bcdae43ce98775e0867a716ff5868064/librt-0.7.4-cp314-cp314t-win32.whl", hash = "sha256:8a461f6456981d8c8e971ff5a55f2e34f4e60871e665d2f5fde23ee74dea4eeb", size = 40276, upload-time = "2025-12-15T16:52:27.54Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/d6/d8823e01bd069934525fddb343189c008b39828a429b473fb20d67d5cd36/librt-0.7.4-cp314-cp314t-win_amd64.whl", hash = "sha256:721a7b125a817d60bf4924e1eec2a7867bfcf64cfc333045de1df7a0629e4481", size = 46772, upload-time = "2025-12-15T16:52:28.653Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/e9/a0aa60f5322814dd084a89614e9e31139702e342f8459ad8af1984a18168/librt-0.7.4-cp314-cp314t-win_arm64.whl", hash = "sha256:76b2ba71265c0102d11458879b4d53ccd0b32b0164d14deb8d2b598a018e502f", size = 39724, upload-time = "2025-12-15T16:52:29.836Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1160,48 +1174,48 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.19.0"
|
||||
version = "1.19.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "librt" },
|
||||
{ name = "librt", marker = "platform_python_implementation != 'PyPy'" },
|
||||
{ name = "mypy-extensions" },
|
||||
{ name = "pathspec" },
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f9/b5/b58cdc25fadd424552804bf410855d52324183112aa004f0732c5f6324cf/mypy-1.19.0.tar.gz", hash = "sha256:f6b874ca77f733222641e5c46e4711648c4037ea13646fd0cdc814c2eaec2528", size = 3579025, upload-time = "2025-11-28T15:49:01.26Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/98/8f/55fb488c2b7dabd76e3f30c10f7ab0f6190c1fcbc3e97b1e588ec625bbe2/mypy-1.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6148ede033982a8c5ca1143de34c71836a09f105068aaa8b7d5edab2b053e6c8", size = 13093239, upload-time = "2025-11-28T15:45:11.342Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/1b/278beea978456c56b3262266274f335c3ba5ff2c8108b3b31bec1ffa4c1d/mypy-1.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a9ac09e52bb0f7fb912f5d2a783345c72441a08ef56ce3e17c1752af36340a39", size = 12156128, upload-time = "2025-11-28T15:46:02.566Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/f8/e06f951902e136ff74fd7a4dc4ef9d884faeb2f8eb9c49461235714f079f/mypy-1.19.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f7254c15ab3f8ed68f8e8f5cbe88757848df793e31c36aaa4d4f9783fd08ab", size = 12753508, upload-time = "2025-11-28T15:44:47.538Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/5a/d035c534ad86e09cee274d53cf0fd769c0b29ca6ed5b32e205be3c06878c/mypy-1.19.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318ba74f75899b0e78b847d8c50821e4c9637c79d9a59680fc1259f29338cb3e", size = 13507553, upload-time = "2025-11-28T15:44:39.26Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/17/c4a5498e00071ef29e483a01558b285d086825b61cf1fb2629fbdd019d94/mypy-1.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cf7d84f497f78b682edd407f14a7b6e1a2212b433eedb054e2081380b7395aa3", size = 13792898, upload-time = "2025-11-28T15:44:31.102Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/f6/bb542422b3ee4399ae1cdc463300d2d91515ab834c6233f2fd1d52fa21e0/mypy-1.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:c3385246593ac2b97f155a0e9639be906e73534630f663747c71908dfbf26134", size = 10048835, upload-time = "2025-11-28T15:48:15.744Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/d2/010fb171ae5ac4a01cc34fbacd7544531e5ace95c35ca166dd8fd1b901d0/mypy-1.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a31e4c28e8ddb042c84c5e977e28a21195d086aaffaf08b016b78e19c9ef8106", size = 13010563, upload-time = "2025-11-28T15:48:23.975Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/6b/63f095c9f1ce584fdeb595d663d49e0980c735a1d2004720ccec252c5d47/mypy-1.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34ec1ac66d31644f194b7c163d7f8b8434f1b49719d403a5d26c87fff7e913f7", size = 12077037, upload-time = "2025-11-28T15:47:51.582Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/83/6cb93d289038d809023ec20eb0b48bbb1d80af40511fa077da78af6ff7c7/mypy-1.19.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb64b0ba5980466a0f3f9990d1c582bcab8db12e29815ecb57f1408d99b4bff7", size = 12680255, upload-time = "2025-11-28T15:46:57.628Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/db/d217815705987d2cbace2edd9100926196d6f85bcb9b5af05058d6e3c8ad/mypy-1.19.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:120cffe120cca5c23c03c77f84abc0c14c5d2e03736f6c312480020082f1994b", size = 13421472, upload-time = "2025-11-28T15:47:59.655Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/51/d2beaca7c497944b07594f3f8aad8d2f0e8fc53677059848ae5d6f4d193e/mypy-1.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7a500ab5c444268a70565e374fc803972bfd1f09545b13418a5174e29883dab7", size = 13651823, upload-time = "2025-11-28T15:45:29.318Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/d1/7883dcf7644db3b69490f37b51029e0870aac4a7ad34d09ceae709a3df44/mypy-1.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:c14a98bc63fd867530e8ec82f217dae29d0550c86e70debc9667fff1ec83284e", size = 10049077, upload-time = "2025-11-28T15:45:39.818Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/7e/1afa8fb188b876abeaa14460dc4983f909aaacaa4bf5718c00b2c7e0b3d5/mypy-1.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0fb3115cb8fa7c5f887c8a8d81ccdcb94cff334684980d847e5a62e926910e1d", size = 13207728, upload-time = "2025-11-28T15:46:26.463Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/13/f103d04962bcbefb1644f5ccb235998b32c337d6c13145ea390b9da47f3e/mypy-1.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3e19e3b897562276bb331074d64c076dbdd3e79213f36eed4e592272dabd760", size = 12202945, upload-time = "2025-11-28T15:48:49.143Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/93/a86a5608f74a22284a8ccea8592f6e270b61f95b8588951110ad797c2ddd/mypy-1.19.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9d491295825182fba01b6ffe2c6fe4e5a49dbf4e2bb4d1217b6ced3b4797bc6", size = 12718673, upload-time = "2025-11-28T15:47:37.193Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/58/cf08fff9ced0423b858f2a7495001fda28dc058136818ee9dffc31534ea9/mypy-1.19.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6016c52ab209919b46169651b362068f632efcd5eb8ef9d1735f6f86da7853b2", size = 13608336, upload-time = "2025-11-28T15:48:32.625Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/ed/9c509105c5a6d4b73bb08733102a3ea62c25bc02c51bca85e3134bf912d3/mypy-1.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f188dcf16483b3e59f9278c4ed939ec0254aa8a60e8fc100648d9ab5ee95a431", size = 13833174, upload-time = "2025-11-28T15:45:48.091Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/71/01939b66e35c6f8cb3e6fdf0b657f0fd24de2f8ba5e523625c8e72328208/mypy-1.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:0e3c3d1e1d62e678c339e7ade72746a9e0325de42cd2cccc51616c7b2ed1a018", size = 10112208, upload-time = "2025-11-28T15:46:41.702Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/0d/a1357e6bb49e37ce26fcf7e3cc55679ce9f4ebee0cd8b6ee3a0e301a9210/mypy-1.19.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7686ed65dbabd24d20066f3115018d2dce030d8fa9db01aa9f0a59b6813e9f9e", size = 13191993, upload-time = "2025-11-28T15:47:22.336Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/75/8e5d492a879ec4490e6ba664b5154e48c46c85b5ac9785792a5ec6a4d58f/mypy-1.19.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fd4a985b2e32f23bead72e2fb4bbe5d6aceee176be471243bd831d5b2644672d", size = 12174411, upload-time = "2025-11-28T15:44:55.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/31/ad5dcee9bfe226e8eaba777e9d9d251c292650130f0450a280aec3485370/mypy-1.19.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc51a5b864f73a3a182584b1ac75c404396a17eced54341629d8bdcb644a5bba", size = 12727751, upload-time = "2025-11-28T15:44:14.169Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/06/b6b8994ce07405f6039701f4b66e9d23f499d0b41c6dd46ec28f96d57ec3/mypy-1.19.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:37af5166f9475872034b56c5efdcf65ee25394e9e1d172907b84577120714364", size = 13593323, upload-time = "2025-11-28T15:46:34.699Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/b1/126e274484cccdf099a8e328d4fda1c7bdb98a5e888fa6010b00e1bbf330/mypy-1.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:510c014b722308c9bd377993bcbf9a07d7e0692e5fa8fc70e639c1eb19fc6bee", size = 13818032, upload-time = "2025-11-28T15:46:18.286Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/56/53a8f70f562dfc466c766469133a8a4909f6c0012d83993143f2a9d48d2d/mypy-1.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:cabbee74f29aa9cd3b444ec2f1e4fa5a9d0d746ce7567a6a609e224429781f53", size = 10120644, upload-time = "2025-11-28T15:47:43.99Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/f4/7751f32f56916f7f8c229fe902cbdba3e4dd3f3ea9e8b872be97e7fc546d/mypy-1.19.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f2e36bed3c6d9b5f35d28b63ca4b727cb0228e480826ffc8953d1892ddc8999d", size = 13185236, upload-time = "2025-11-28T15:45:20.696Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/31/871a9531f09e78e8d145032355890384f8a5b38c95a2c7732d226b93242e/mypy-1.19.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a18d8abdda14035c5718acb748faec09571432811af129bf0d9e7b2d6699bf18", size = 12213902, upload-time = "2025-11-28T15:46:10.117Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/b8/af221910dd40eeefa2077a59107e611550167b9994693fc5926a0b0f87c0/mypy-1.19.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75e60aca3723a23511948539b0d7ed514dda194bc3755eae0bfc7a6b4887aa7", size = 12738600, upload-time = "2025-11-28T15:44:22.521Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/9f/c39e89a3e319c1d9c734dedec1183b2cc3aefbab066ec611619002abb932/mypy-1.19.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f44f2ae3c58421ee05fe609160343c25f70e3967f6e32792b5a78006a9d850f", size = 13592639, upload-time = "2025-11-28T15:48:08.55Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/6d/ffaf5f01f5e284d9033de1267e6c1b8f3783f2cf784465378a86122e884b/mypy-1.19.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63ea6a00e4bd6822adbfc75b02ab3653a17c02c4347f5bb0cf1d5b9df3a05835", size = 13799132, upload-time = "2025-11-28T15:47:06.032Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/b0/c33921e73aaa0106224e5a34822411bea38046188eb781637f5a5b07e269/mypy-1.19.0-cp314-cp314-win_amd64.whl", hash = "sha256:3ad925b14a0bb99821ff6f734553294aa6a3440a8cb082fe1f5b84dfb662afb1", size = 10269832, upload-time = "2025-11-28T15:47:29.392Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/0e/fe228ed5aeab470c6f4eb82481837fadb642a5aa95cc8215fd2214822c10/mypy-1.19.0-py3-none-any.whl", hash = "sha256:0c01c99d626380752e527d5ce8e69ffbba2046eb8a060db0329690849cf9b6f9", size = 2469714, upload-time = "2025-11-28T15:45:33.22Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1392,7 +1406,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "pre-commit"
|
||||
version = "4.5.0"
|
||||
version = "4.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cfgv" },
|
||||
|
|
@ -1401,9 +1415,9 @@ dependencies = [
|
|||
{ name = "pyyaml" },
|
||||
{ name = "virtualenv" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f4/9b/6a4ffb4ed980519da959e1cf3122fc6cb41211daa58dbae1c73c0e519a37/pre_commit-4.5.0.tar.gz", hash = "sha256:dc5a065e932b19fc1d4c653c6939068fe54325af8e741e74e88db4d28a4dd66b", size = 198428, upload-time = "2025-11-22T21:02:42.304Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/c4/b2d28e9d2edf4f1713eb3c29307f1a63f3d67cf09bdda29715a36a68921a/pre_commit-4.5.0-py2.py3-none-any.whl", hash = "sha256:25e2ce09595174d9c97860a95609f9f852c0614ba602de3561e267547f2335e1", size = 226429, upload-time = "2025-11-22T21:02:40.836Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1700,38 +1714,38 @@ wheels = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "roman-numerals-py"
|
||||
version = "3.1.0"
|
||||
name = "roman-numerals"
|
||||
version = "4.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d", size = 9017, upload-time = "2025-02-22T07:34:54.333Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ae/f9/41dc953bbeb056c17d5f7a519f50fdf010bd0553be2d630bc69d1e022703/roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2", size = 9077, upload-time = "2025-12-17T18:25:34.381Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742, upload-time = "2025-02-22T07:34:52.422Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/54/6f679c435d28e0a568d8e8a7c0a93a09010818634c3c3907fc98d8983770/roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7", size = 7676, upload-time = "2025-12-17T18:25:33.098Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.14.9"
|
||||
version = "0.14.10"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f6/1b/ab712a9d5044435be8e9a2beb17cbfa4c241aa9b5e4413febac2a8b79ef2/ruff-0.14.9.tar.gz", hash = "sha256:35f85b25dd586381c0cc053f48826109384c81c00ad7ef1bd977bfcc28119d5b", size = 5809165, upload-time = "2025-12-11T21:39:47.381Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/57/08/52232a877978dd8f9cf2aeddce3e611b40a63287dfca29b6b8da791f5e8d/ruff-0.14.10.tar.gz", hash = "sha256:9a2e830f075d1a42cd28420d7809ace390832a490ed0966fe373ba288e77aaf4", size = 5859763, upload-time = "2025-12-18T19:28:57.98Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/1c/d1b1bba22cffec02351c78ab9ed4f7d7391876e12720298448b29b7229c1/ruff-0.14.9-py3-none-linux_armv6l.whl", hash = "sha256:f1ec5de1ce150ca6e43691f4a9ef5c04574ad9ca35c8b3b0e18877314aba7e75", size = 13576541, upload-time = "2025-12-11T21:39:14.806Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/ab/ffe580e6ea1fca67f6337b0af59fc7e683344a43642d2d55d251ff83ceae/ruff-0.14.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ed9d7417a299fc6030b4f26333bf1117ed82a61ea91238558c0268c14e00d0c2", size = 13779363, upload-time = "2025-12-11T21:39:20.29Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/f8/2be49047f929d6965401855461e697ab185e1a6a683d914c5c19c7962d9e/ruff-0.14.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d5dc3473c3f0e4a1008d0ef1d75cee24a48e254c8bed3a7afdd2b4392657ed2c", size = 12925292, upload-time = "2025-12-11T21:39:38.757Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/e9/08840ff5127916bb989c86f18924fd568938b06f58b60e206176f327c0fe/ruff-0.14.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84bf7c698fc8f3cb8278830fb6b5a47f9bcc1ed8cb4f689b9dd02698fa840697", size = 13362894, upload-time = "2025-12-11T21:39:02.524Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/1c/5b4e8e7750613ef43390bb58658eaf1d862c0cc3352d139cd718a2cea164/ruff-0.14.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aa733093d1f9d88a5d98988d8834ef5d6f9828d03743bf5e338bf980a19fce27", size = 13311482, upload-time = "2025-12-11T21:39:17.51Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/3a/459dce7a8cb35ba1ea3e9c88f19077667a7977234f3b5ab197fad240b404/ruff-0.14.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a1cfb04eda979b20c8c19550c8b5f498df64ff8da151283311ce3199e8b3648", size = 14016100, upload-time = "2025-12-11T21:39:41.948Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/31/f064f4ec32524f9956a0890fc6a944e5cf06c63c554e39957d208c0ffc45/ruff-0.14.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1e5cb521e5ccf0008bd74d5595a4580313844a42b9103b7388eca5a12c970743", size = 15477729, upload-time = "2025-12-11T21:39:23.279Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/6d/f364252aad36ccd443494bc5f02e41bf677f964b58902a17c0b16c53d890/ruff-0.14.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd429a8926be6bba4befa8cdcf3f4dd2591c413ea5066b1e99155ed245ae42bb", size = 15122386, upload-time = "2025-12-11T21:39:33.125Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/02/e848787912d16209aba2799a4d5a1775660b6a3d0ab3944a4ccc13e64a02/ruff-0.14.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab208c1b7a492e37caeaf290b1378148f75e13c2225af5d44628b95fd7834273", size = 14497124, upload-time = "2025-12-11T21:38:59.33Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/51/0489a6a5595b7760b5dbac0dd82852b510326e7d88d51dbffcd2e07e3ff3/ruff-0.14.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72034534e5b11e8a593f517b2f2f2b273eb68a30978c6a2d40473ad0aaa4cb4a", size = 14195343, upload-time = "2025-12-11T21:39:44.866Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/53/3bb8d2fa73e4c2f80acc65213ee0830fa0c49c6479313f7a68a00f39e208/ruff-0.14.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:712ff04f44663f1b90a1195f51525836e3413c8a773574a7b7775554269c30ed", size = 14346425, upload-time = "2025-12-11T21:39:05.927Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/04/bdb1d0ab876372da3e983896481760867fc84f969c5c09d428e8f01b557f/ruff-0.14.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a111fee1db6f1d5d5810245295527cda1d367c5aa8f42e0fca9a78ede9b4498b", size = 13258768, upload-time = "2025-12-11T21:39:08.691Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/d9/8bf8e1e41a311afd2abc8ad12be1b6c6c8b925506d9069b67bb5e9a04af3/ruff-0.14.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8769efc71558fecc25eb295ddec7d1030d41a51e9dcf127cbd63ec517f22d567", size = 13326939, upload-time = "2025-12-11T21:39:53.842Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/56/a213fa9edb6dd849f1cfbc236206ead10913693c72a67fb7ddc1833bf95d/ruff-0.14.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:347e3bf16197e8a2de17940cd75fd6491e25c0aa7edf7d61aa03f146a1aa885a", size = 13578888, upload-time = "2025-12-11T21:39:35.988Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/09/6a4a67ffa4abae6bf44c972a4521337ffce9cbc7808faadede754ef7a79c/ruff-0.14.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7715d14e5bccf5b660f54516558aa94781d3eb0838f8e706fb60e3ff6eff03a8", size = 14314473, upload-time = "2025-12-11T21:39:50.78Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/0d/15cc82da5d83f27a3c6b04f3a232d61bc8c50d38a6cd8da79228e5f8b8d6/ruff-0.14.9-py3-none-win32.whl", hash = "sha256:df0937f30aaabe83da172adaf8937003ff28172f59ca9f17883b4213783df197", size = 13202651, upload-time = "2025-12-11T21:39:26.628Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/f7/c78b060388eefe0304d9d42e68fab8cffd049128ec466456cef9b8d4f06f/ruff-0.14.9-py3-none-win_amd64.whl", hash = "sha256:c0b53a10e61df15a42ed711ec0bda0c582039cf6c754c49c020084c55b5b0bc2", size = 14702079, upload-time = "2025-12-11T21:39:11.954Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/09/7a9520315decd2334afa65ed258fed438f070e31f05a2e43dd480a5e5911/ruff-0.14.9-py3-none-win_arm64.whl", hash = "sha256:8e821c366517a074046d92f0e9213ed1c13dbc5b37a7fc20b07f79b64d62cc84", size = 13744730, upload-time = "2025-12-11T21:39:29.659Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/01/933704d69f3f05ee16ef11406b78881733c186fe14b6a46b05cfcaf6d3b2/ruff-0.14.10-py3-none-linux_armv6l.whl", hash = "sha256:7a3ce585f2ade3e1f29ec1b92df13e3da262178df8c8bdf876f48fa0e8316c49", size = 13527080, upload-time = "2025-12-18T19:29:25.642Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/58/a0349197a7dfa603ffb7f5b0470391efa79ddc327c1e29c4851e85b09cc5/ruff-0.14.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:674f9be9372907f7257c51f1d4fc902cb7cf014b9980152b802794317941f08f", size = 13797320, upload-time = "2025-12-18T19:29:02.571Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/82/36be59f00a6082e38c23536df4e71cdbc6af8d7c707eade97fcad5c98235/ruff-0.14.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d85713d522348837ef9df8efca33ccb8bd6fcfc86a2cde3ccb4bc9d28a18003d", size = 12918434, upload-time = "2025-12-18T19:28:51.202Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/00/45c62a7f7e34da92a25804f813ebe05c88aa9e0c25e5cb5a7d23dd7450e3/ruff-0.14.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6987ebe0501ae4f4308d7d24e2d0fe3d7a98430f5adfd0f1fead050a740a3a77", size = 13371961, upload-time = "2025-12-18T19:29:04.991Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/31/a5906d60f0405f7e57045a70f2d57084a93ca7425f22e1d66904769d1628/ruff-0.14.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16a01dfb7b9e4eee556fbfd5392806b1b8550c9b4a9f6acd3dbe6812b193c70a", size = 13275629, upload-time = "2025-12-18T19:29:21.381Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/60/61c0087df21894cf9d928dc04bcd4fb10e8b2e8dca7b1a276ba2155b2002/ruff-0.14.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7165d31a925b7a294465fa81be8c12a0e9b60fb02bf177e79067c867e71f8b1f", size = 14029234, upload-time = "2025-12-18T19:29:00.132Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/84/77d911bee3b92348b6e5dab5a0c898d87084ea03ac5dc708f46d88407def/ruff-0.14.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c561695675b972effb0c0a45db233f2c816ff3da8dcfbe7dfc7eed625f218935", size = 15449890, upload-time = "2025-12-18T19:28:53.573Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/36/480206eaefa24a7ec321582dda580443a8f0671fdbf6b1c80e9c3e93a16a/ruff-0.14.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bb98fcbbc61725968893682fd4df8966a34611239c9fd07a1f6a07e7103d08e", size = 15123172, upload-time = "2025-12-18T19:29:23.453Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/38/68e414156015ba80cef5473d57919d27dfb62ec804b96180bafdeaf0e090/ruff-0.14.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f24b47993a9d8cb858429e97bdf8544c78029f09b520af615c1d261bf827001d", size = 14460260, upload-time = "2025-12-18T19:29:27.808Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/19/9e050c0dca8aba824d67cc0db69fb459c28d8cd3f6855b1405b3f29cc91d/ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59aabd2e2c4fd614d2862e7939c34a532c04f1084476d6833dddef4afab87e9f", size = 14229978, upload-time = "2025-12-18T19:29:11.32Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/eb/e8dd1dd6e05b9e695aa9dd420f4577debdd0f87a5ff2fedda33c09e9be8c/ruff-0.14.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:213db2b2e44be8625002dbea33bb9c60c66ea2c07c084a00d55732689d697a7f", size = 14338036, upload-time = "2025-12-18T19:29:09.184Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/12/f3e3a505db7c19303b70af370d137795fcfec136d670d5de5391e295c134/ruff-0.14.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b914c40ab64865a17a9a5b67911d14df72346a634527240039eb3bd650e5979d", size = 13264051, upload-time = "2025-12-18T19:29:13.431Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/64/8c3a47eaccfef8ac20e0484e68e0772013eb85802f8a9f7603ca751eb166/ruff-0.14.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1484983559f026788e3a5c07c81ef7d1e97c1c78ed03041a18f75df104c45405", size = 13283998, upload-time = "2025-12-18T19:29:06.994Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/84/534a5506f4074e5cc0529e5cd96cfc01bb480e460c7edf5af70d2bcae55e/ruff-0.14.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c70427132db492d25f982fffc8d6c7535cc2fd2c83fc8888f05caaa248521e60", size = 13601891, upload-time = "2025-12-18T19:28:55.811Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/1e/14c916087d8598917dbad9b2921d340f7884824ad6e9c55de948a93b106d/ruff-0.14.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5bcf45b681e9f1ee6445d317ce1fa9d6cba9a6049542d1c3d5b5958986be8830", size = 14336660, upload-time = "2025-12-18T19:29:16.531Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/1c/d7b67ab43f30013b47c12b42d1acd354c195351a3f7a1d67f59e54227ede/ruff-0.14.10-py3-none-win32.whl", hash = "sha256:104c49fc7ab73f3f3a758039adea978869a918f31b73280db175b43a2d9b51d6", size = 13196187, upload-time = "2025-12-18T19:29:19.006Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/9c/896c862e13886fae2af961bef3e6312db9ebc6adc2b156fe95e615dee8c1/ruff-0.14.10-py3-none-win_amd64.whl", hash = "sha256:466297bd73638c6bdf06485683e812db1c00c7ac96d4ddd0294a338c62fdc154", size = 14661283, upload-time = "2025-12-18T19:29:30.16Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/31/b0e29d572670dca3674eeee78e418f20bdf97fa8aa9ea71380885e175ca0/ruff-0.14.10-py3-none-win_arm64.whl", hash = "sha256:e51d046cf6dda98a4633b8a8a771451107413b0f07183b2bef03f075599e44e6", size = 13729839, upload-time = "2025-12-18T19:28:48.636Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1745,11 +1759,11 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "soupsieve"
|
||||
version = "2.8"
|
||||
version = "2.8.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472, upload-time = "2025-08-27T15:39:51.78Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/89/23/adf3796d740536d63a6fbda113d07e60c734b6ed5d3058d1e47fc0495e47/soupsieve-2.8.1.tar.gz", hash = "sha256:4cf733bc50fa805f5df4b8ef4740fc0e0fa6218cf3006269afd3f9d6d80fd350", size = 117856, upload-time = "2025-12-18T13:50:34.655Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl", hash = "sha256:a11fe2a6f3d76ab3cf2de04eb339c1be5b506a8a47f2ceb6d139803177f85434", size = 36710, upload-time = "2025-12-18T13:50:33.267Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1785,7 +1799,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "sphinx"
|
||||
version = "8.2.3"
|
||||
version = "9.0.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12'",
|
||||
|
|
@ -1801,7 +1815,7 @@ dependencies = [
|
|||
{ name = "packaging", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "pygments", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "requests", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "roman-numerals-py", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "roman-numerals", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "snowballstemmer", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.11'" },
|
||||
|
|
@ -1810,9 +1824,9 @@ dependencies = [
|
|||
{ name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876, upload-time = "2025-03-02T22:31:59.658Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size = 8710502, upload-time = "2025-12-04T07:45:27.343Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741, upload-time = "2025-03-02T22:31:56.836Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/3f/4bbd76424c393caead2e1eb89777f575dee5c8653e2d4b6afd7a564f5974/sphinx-9.0.4-py3-none-any.whl", hash = "sha256:5bebc595a5e943ea248b99c13814c1c5e10b3ece718976824ffa7959ff95fffb", size = 3917713, upload-time = "2025-12-04T07:45:24.944Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1845,7 +1859,7 @@ resolution-markers = [
|
|||
]
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "starlette", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "uvicorn", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "watchfiles", marker = "python_full_version >= '3.11'" },
|
||||
|
|
@ -1862,7 +1876,7 @@ version = "1.0.0b2"
|
|||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||
{ name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/98/0b/a866924ded68efec7a1759587a4e478aec7559d8165fac8b2ad1c0e774d6/sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9", size = 20736, upload-time = "2023-07-08T18:40:54.166Z" }
|
||||
wheels = [
|
||||
|
|
@ -1896,7 +1910,7 @@ dependencies = [
|
|||
{ name = "django", version = "6.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
||||
{ name = "pprintpp" },
|
||||
{ name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||
{ name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/dc/28/92f6d685899fbd74a6c575c50dcc1abb8ab69c6da0160bc99d557d2104d1/sphinxcontrib-django-2.5.tar.gz", hash = "sha256:45a54c0cc1f641d6c15872828862f0738348ca8d7d5b92777bcaa530678c2cc4", size = 23788, upload-time = "2023-09-26T17:54:36.259Z" }
|
||||
wheels = [
|
||||
|
|
@ -1941,11 +1955,11 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "sqlparse"
|
||||
version = "0.5.4"
|
||||
version = "0.5.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/18/67/701f86b28d63b2086de47c942eccf8ca2208b3be69715a1119a4e384415a/sqlparse-0.5.4.tar.gz", hash = "sha256:4396a7d3cf1cd679c1be976cf3dc6e0a51d0111e87787e7a8d780e7d5a998f9e", size = 120112, upload-time = "2025-11-28T07:10:18.377Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/25/70/001ee337f7aa888fb2e3f5fd7592a6afc5283adb1ed44ce8df5764070f22/sqlparse-0.5.4-py3-none-any.whl", hash = "sha256:99a9f0314977b76d776a0fcb8554de91b9bb8a18560631d6bc48721d07023dcb", size = 45933, upload-time = "2025-11-28T07:10:19.73Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2108,11 +2122,11 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2025.2"
|
||||
version = "2025.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2126,28 +2140,28 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "uv"
|
||||
version = "0.9.17"
|
||||
version = "0.9.18"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/52/1a/cb0c37ae8513b253bcbc13d42392feb7d95ea696eb398b37535a28df9040/uv-0.9.17.tar.gz", hash = "sha256:6d93ab9012673e82039cfa7f9f66f69b388bc3f910f9e8a2ebee211353f620aa", size = 3815957, upload-time = "2025-12-09T23:01:21.756Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e3/03/1afff9e6362dc9d3a9e03743da0a4b4c7a0809f859c79eb52bbae31ea582/uv-0.9.18.tar.gz", hash = "sha256:17b5502f7689c4dc1fdeee9d8437a9a6664dcaa8476e70046b5f4753559533f5", size = 3824466, upload-time = "2025-12-16T15:45:11.81Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/e2/b6e2d473bdc37f4d86307151b53c0776e9925de7376ce297e92eab2e8894/uv-0.9.17-py3-none-linux_armv6l.whl", hash = "sha256:c708e6560ae5bc3cda1ba93f0094148ce773b6764240ced433acf88879e57a67", size = 21254511, upload-time = "2025-12-09T23:00:36.604Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/40/75f1529a8bf33cc5c885048e64a014c3096db5ac7826c71e20f2b731b588/uv-0.9.17-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:233b3d90f104c59d602abf434898057876b87f64df67a37129877d6dab6e5e10", size = 20384366, upload-time = "2025-12-09T23:01:17.293Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/30/b3a343893681a569cbb74f8747a1c24e5f18ca9e07de0430aceaf9389ef4/uv-0.9.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4b8e5513d48a267bfa180ca7fefaf6f27b1267e191573b3dba059981143e88ef", size = 18924624, upload-time = "2025-12-09T23:01:10.291Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/56/9daf8bbe4a9a36eb0b9257cf5e1e20f9433d0ce996778ccf1929cbe071a4/uv-0.9.17-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:8f283488bbcf19754910cc1ae7349c567918d6367c596e5a75d4751e0080eee0", size = 20671687, upload-time = "2025-12-09T23:00:51.927Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/c8/4050ff7dc692770092042fcef57223b8852662544f5981a7f6cac8fc488d/uv-0.9.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9cf8052ba669dc17bdba75dae655094d820f4044990ea95c01ec9688c182f1da", size = 20861866, upload-time = "2025-12-09T23:01:12.555Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/d4/208e62b7db7a65cb3390a11604c59937e387d07ed9f8b63b54edb55e2292/uv-0.9.17-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06749461b11175a884be193120044e7f632a55e2624d9203398808907d346aad", size = 21858420, upload-time = "2025-12-09T23:01:00.009Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/2c/91288cd5a04db37dfc1e0dad26ead84787db5832d9836b4cc8e0fa7f3c53/uv-0.9.17-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:35eb1a519688209160e48e1bb8032d36d285948a13b4dd21afe7ec36dc2a9787", size = 23471658, upload-time = "2025-12-09T23:00:49.503Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/ba/493eba650ffad1df9e04fd8eabfc2d0aebc23e8f378acaaee9d95ca43518/uv-0.9.17-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2bfb60a533e82690ab17dfe619ff7f294d053415645800d38d13062170230714", size = 23062950, upload-time = "2025-12-09T23:00:39.055Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/9e/f7f679503c06843ba59451e3193f35fb7c782ff0afc697020d4718a7de46/uv-0.9.17-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd0f3e380ff148aff3d769e95a9743cb29c7f040d7ef2896cafe8063279a6bc1", size = 22080299, upload-time = "2025-12-09T23:00:44.026Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/2e/76ba33c7d9efe9f17480db1b94d3393025062005e346bb8b3660554526da/uv-0.9.17-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd2c3d25fbd8f91b30d0fac69a13b8e2c2cd8e606d7e6e924c1423e4ff84e616", size = 22087554, upload-time = "2025-12-09T23:00:41.715Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/db/ef4aae4a6c49076db2acd2a7b0278ddf3dbf785d5172b3165018b96ba2fb/uv-0.9.17-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:330e7085857e4205c5196a417aca81cfbfa936a97dd2a0871f6560a88424ebf2", size = 20823225, upload-time = "2025-12-09T23:00:57.041Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/73/e0f816cacd802a1cb25e71de9d60e57fa1f6c659eb5599cef708668618cc/uv-0.9.17-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:45880faa9f6cf91e3cda4e5f947da6a1004238fdc0ed4ebc18783a12ce197312", size = 22004893, upload-time = "2025-12-09T23:01:15.011Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/6b/700f6256ee191136eb06e40d16970a4fc687efdccf5e67c553a258063019/uv-0.9.17-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:8e775a1b94c6f248e22f0ce2f86ed37c24e10ae31fb98b7e1b9f9a3189d25991", size = 20853850, upload-time = "2025-12-09T23:01:02.694Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/6a/13f02e2ed6510223c40f74804586b09e5151d9319f93aab1e49d91db13bb/uv-0.9.17-py3-none-musllinux_1_1_i686.whl", hash = "sha256:8650c894401ec96488a6fd84a5b4675e09be102f5525c902a12ba1c8ef8ff230", size = 21322623, upload-time = "2025-12-09T23:00:46.806Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/18/2d19780cebfbec877ea645463410c17859f8070f79c1a34568b153d78e1d/uv-0.9.17-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:673066b72d8b6c86be0dae6d5f73926bcee8e4810f1690d7b8ce5429d919cde3", size = 22290123, upload-time = "2025-12-09T23:00:54.394Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/69/ab79bde3f7b6d2ac89f839ea40411a9cf3e67abede2278806305b6ba797e/uv-0.9.17-py3-none-win32.whl", hash = "sha256:7407d45afeae12399de048f7c8c2256546899c94bd7892dbddfae6766616f5a3", size = 20070709, upload-time = "2025-12-09T23:01:05.105Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/a0/ab5b1850197bf407d095361b214352e40805441791fed35b891621cb1562/uv-0.9.17-py3-none-win_amd64.whl", hash = "sha256:22fcc26755abebdf366becc529b2872a831ce8bb14b36b6a80d443a1d7f84d3b", size = 22122852, upload-time = "2025-12-09T23:01:07.783Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/ef/813cfedda3c8e49d8b59a41c14fcc652174facfd7a1caf9fee162b40ccbd/uv-0.9.17-py3-none-win_arm64.whl", hash = "sha256:6761076b27a763d0ede2f5e72455d2a46968ff334badf8312bb35988c5254831", size = 20435751, upload-time = "2025-12-09T23:01:19.732Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/9c/92fad10fcee8ea170b66442d95fd2af308fe9a107909ded4b3cc384fdc69/uv-0.9.18-py3-none-linux_armv6l.whl", hash = "sha256:e9e4915bb280c1f79b9a1c16021e79f61ed7c6382856ceaa99d53258cb0b4951", size = 21345538, upload-time = "2025-12-16T15:45:13.992Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/b1/b0e5808e05acb54aa118c625d9f7b117df614703b0cbb89d419d03d117f3/uv-0.9.18-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d91abfd2649987996e3778729140c305ef0f6ff5909f55aac35c3c372544a24f", size = 20439572, upload-time = "2025-12-16T15:45:26.397Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/0b/9487d83adf5b7fd1e20ced33f78adf84cb18239c3d7e91f224cedba46c08/uv-0.9.18-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cf33f4146fd97e94cdebe6afc5122208eea8c55b65ca4127f5a5643c9717c8b8", size = 18952907, upload-time = "2025-12-16T15:44:48.399Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/92/c8f7ae8900eff8e4ce1f7826d2e1e2ad5a95a5f141abdb539865aff79930/uv-0.9.18-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:edf965e9a5c55f74020ac82285eb0dfe7fac4f325ad0a7afc816290269ecfec1", size = 20772495, upload-time = "2025-12-16T15:45:29.614Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/28/9831500317c1dd6cde5099e3eb3b22b88ac75e47df7b502f6aef4df5750e/uv-0.9.18-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae10a941bd7ca1ee69edbe3998c34dce0a9fc2d2406d98198343daf7d2078493", size = 20949623, upload-time = "2025-12-16T15:44:57.482Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/ff/1fe1ffa69c8910e54dd11f01fb0765d4fd537ceaeb0c05fa584b6b635b82/uv-0.9.18-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1669a95b588f613b13dd10e08ced6d5bcd79169bba29a2240eee87532648790", size = 21920580, upload-time = "2025-12-16T15:44:39.009Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/ee/eed3ec7679ee80e16316cfc95ed28ef6851700bcc66edacfc583cbd2cc47/uv-0.9.18-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:11e1e406590d3159138288203a41ff8a8904600b8628a57462f04ff87d62c477", size = 23491234, upload-time = "2025-12-16T15:45:32.59Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/58/64b15df743c79ad03ea7fbcbd27b146ba16a116c57f557425dd4e44d6684/uv-0.9.18-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e82078d3c622cb4c60da87f156168ffa78b9911136db7ffeb8e5b0a040bf30e", size = 23095438, upload-time = "2025-12-16T15:45:17.916Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/6d/3d3dae71796961603c3871699e10d6b9de2e65a3c327b58d4750610a5f93/uv-0.9.18-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704abaf6e76b4d293fc1f24bef2c289021f1df0de9ed351f476cbbf67a7edae0", size = 22140992, upload-time = "2025-12-16T15:44:45.527Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/91/1042d0966a30e937df500daed63e1f61018714406ce4023c8a6e6d2dcf7c/uv-0.9.18-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3332188fd8d96a68e5001409a52156dced910bf1bc41ec3066534cffcd46eb68", size = 22229626, upload-time = "2025-12-16T15:45:20.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/1f/0a4a979bb2bf6e1292cc57882955bf1d7757cad40b1862d524c59c2a2ad8/uv-0.9.18-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:b7295e6d505f1fd61c54b1219e3b18e11907396333a9fa61cefe489c08fc7995", size = 20896524, upload-time = "2025-12-16T15:45:06.799Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/3c/24f92e56af00cac7d9bed2888d99a580f8093c8745395ccf6213bfccf20b/uv-0.9.18-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:62ea0e518dd4ab76e6f06c0f43a25898a6342a3ecf996c12f27f08eb801ef7f1", size = 22077340, upload-time = "2025-12-16T15:44:51.271Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/3e/73163116f748800e676bf30cee838448e74ac4cc2f716c750e1705bc3fe4/uv-0.9.18-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:8bd073e30030211ba01206caa57b4d63714e1adee2c76a1678987dd52f72d44d", size = 20932956, upload-time = "2025-12-16T15:45:00.3Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/1b/a26990b51a17de1ffe41fbf2e30de3a98f0e0bce40cc60829fb9d9ed1a8a/uv-0.9.18-py3-none-musllinux_1_1_i686.whl", hash = "sha256:f248e013d10e1fc7a41f94310628b4a8130886b6d683c7c85c42b5b36d1bcd02", size = 21357247, upload-time = "2025-12-16T15:45:23.575Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/20/b6ba14fdd671e9237b22060d7422aba4a34503e3e42d914dbf925eff19aa/uv-0.9.18-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:17bedf2b0791e87d889e1c7f125bd5de77e4b7579aec372fa06ba832e07c957e", size = 22443585, upload-time = "2025-12-16T15:44:42.213Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/da/1b3dd596964f90a122cfe94dcf5b6b89cf5670eb84434b8c23864382576f/uv-0.9.18-py3-none-win32.whl", hash = "sha256:de6f0bb3e9c18e484545bd1549ec3c956968a141a393d42e2efb25281cb62787", size = 20091088, upload-time = "2025-12-16T15:45:03.225Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/0b/50e13ebc1eedb36d88524b7740f78351be33213073e3faf81ac8925d0c6e/uv-0.9.18-py3-none-win_amd64.whl", hash = "sha256:c82b0e2e36b33e2146fba5f0ae6906b9679b3b5fe6a712e5d624e45e441e58e9", size = 22181193, upload-time = "2025-12-16T15:44:54.394Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/d4/0bf338d863a3d9e5545e268d77a8e6afdd75d26bffc939603042f2e739f9/uv-0.9.18-py3-none-win_arm64.whl", hash = "sha256:4c4ce0ed080440bbda2377488575d426867f94f5922323af6d4728a1cd4d091d", size = 20564933, upload-time = "2025-12-16T15:45:09.819Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user