lino 25.7.3__py3-none-any.whl → 25.8.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lino/__init__.py +1 -1
- lino/core/actions.py +2 -1
- lino/core/dbtables.py +4 -1
- lino/core/site.py +10 -14
- lino/help_texts.py +1 -1
- lino/management/commands/ddt.py +60 -0
- lino/management/commands/dump2py.py +13 -25
- lino/management/commands/prep.py +1 -1
- lino/modlib/comments/mixins.py +8 -6
- lino/modlib/comments/models.py +18 -12
- lino/modlib/comments/ui.py +1 -2
- lino/modlib/memo/mixins.py +2 -0
- lino/modlib/publisher/models.py +15 -8
- lino/utils/cycler.py +6 -3
- lino/utils/dpy.py +70 -59
- {lino-25.7.3.dist-info → lino-25.8.1.dist-info}/METADATA +1 -1
- {lino-25.7.3.dist-info → lino-25.8.1.dist-info}/RECORD +20 -19
- {lino-25.7.3.dist-info → lino-25.8.1.dist-info}/WHEEL +0 -0
- {lino-25.7.3.dist-info → lino-25.8.1.dist-info}/licenses/AUTHORS.rst +0 -0
- {lino-25.7.3.dist-info → lino-25.8.1.dist-info}/licenses/COPYING +0 -0
lino/__init__.py
CHANGED
@@ -31,7 +31,7 @@ from django import VERSION
|
|
31
31
|
from django.apps import AppConfig
|
32
32
|
from django.conf import settings
|
33
33
|
import warnings
|
34
|
-
__version__ = '25.
|
34
|
+
__version__ = '25.8.1'
|
35
35
|
|
36
36
|
# import setuptools # avoid UserWarning "Distutils was imported before Setuptools"?
|
37
37
|
|
lino/core/actions.py
CHANGED
@@ -233,7 +233,7 @@ class Action(Parametrizable, Permittable):
|
|
233
233
|
|
234
234
|
def full_name(self, actor=None):
|
235
235
|
if self.action_name is None:
|
236
|
-
raise Exception("Tried to full_name() on
|
236
|
+
raise Exception(f"Tried to full_name() on {repr(self)}")
|
237
237
|
# ~ return repr(self)
|
238
238
|
if actor is None or (self.parameters and not self.no_params_window):
|
239
239
|
return self.defining_actor.actor_id + "." + self.action_name
|
@@ -362,6 +362,7 @@ class ShowDetail(Action):
|
|
362
362
|
ui5_icon_name = "sap-icon://detail-view"
|
363
363
|
opens_a_window = True
|
364
364
|
window_type = constants.WINDOW_TYPE_DETAIL
|
365
|
+
show_in_toolbar = False
|
365
366
|
show_in_workflow = False
|
366
367
|
save_action_name = "submit_detail"
|
367
368
|
callable_from = "t"
|
lino/core/dbtables.py
CHANGED
@@ -440,7 +440,10 @@ class Table(AbstractTable):
|
|
440
440
|
|
441
441
|
# if self.master is not None:
|
442
442
|
# self.master = resolve_model(self.master)
|
443
|
-
|
443
|
+
|
444
|
+
# For abstract tables we don't check the master key, e.g.
|
445
|
+
# tickets.TicketsByGroup
|
446
|
+
if self.master_key and not self.abstract:
|
444
447
|
master_model = None
|
445
448
|
try:
|
446
449
|
fk = self.model.get_data_elem(self.master_key)
|
lino/core/site.py
CHANGED
@@ -670,18 +670,16 @@ class Site(object):
|
|
670
670
|
"formatter": "verbose",
|
671
671
|
}
|
672
672
|
elif self.use_systemd:
|
673
|
-
|
674
|
-
|
675
|
-
"
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
# except ImportError:
|
684
|
-
# pass
|
673
|
+
try:
|
674
|
+
from systemd.journal import JournalHandler
|
675
|
+
handlers["file"] = {
|
676
|
+
"class": "systemd.journal.JournalHandler",
|
677
|
+
"SYSLOG_IDENTIFIER": str(self.project_name),
|
678
|
+
}
|
679
|
+
except ImportError:
|
680
|
+
# Silenly ignore. Can happen when use_systemd is True but
|
681
|
+
# `pm install` hasn't yet been run.
|
682
|
+
pass
|
685
683
|
|
686
684
|
# when a file handler exists, we have the loggers use it even if this
|
687
685
|
# instance didn't create it:
|
@@ -919,8 +917,6 @@ class Site(object):
|
|
919
917
|
extends_models = pc.__dict__.get("extends_models")
|
920
918
|
if extends_models is not None:
|
921
919
|
for m in extends_models:
|
922
|
-
if "." in m:
|
923
|
-
raise Exception("extends_models in %s still uses '.'" % pc)
|
924
920
|
for pp in plugin_parents(pc):
|
925
921
|
if pp is pc:
|
926
922
|
continue
|
lino/help_texts.py
CHANGED
@@ -232,7 +232,7 @@ help_texts = {
|
|
232
232
|
'lino.utils.djangotest.RestoreTestCase' : _("""Used for testing migrations from previous versions."""),
|
233
233
|
'lino.utils.djangotest.RestoreTestCase.tested_versions' : _("""A list of strings, each string is a version for which there must be a migration dump created by makemigdump."""),
|
234
234
|
'lino.utils.dpy.FakeDeserializedObject' : _("""Imitates DeserializedObject required by loaddata."""),
|
235
|
-
'lino.utils.dpy.FakeDeserializedObject.try_save' : _("""Try to save
|
235
|
+
'lino.utils.dpy.FakeDeserializedObject.try_save' : _("""Try to save this Model instance."""),
|
236
236
|
'lino.utils.dpy.Serializer' : _("""Serializes a QuerySet to a py stream."""),
|
237
237
|
'lino.utils.dpy.FlushDeferredObjects' : _("""Indicator class object. Fixture may yield a FlushDeferredObjects to indicate that all deferred objects should get saved before going on."""),
|
238
238
|
'lino.utils.dpy.DpyLoader' : _("""Instantiated by restore.py."""),
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
2
|
+
# Copyright 2025 Rumma & Ko Ltd
|
3
|
+
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
|
+
|
5
|
+
# import shutil
|
6
|
+
from click import confirm
|
7
|
+
import subprocess
|
8
|
+
from django.db import DEFAULT_DB_ALIAS
|
9
|
+
from django.core.management import call_command
|
10
|
+
from django.core.management.base import BaseCommand, CommandError
|
11
|
+
from django.conf import settings
|
12
|
+
|
13
|
+
|
14
|
+
class Command(BaseCommand):
|
15
|
+
help = "Run a double-dump test on this site."
|
16
|
+
|
17
|
+
def add_arguments(self, parser):
|
18
|
+
super().add_arguments(parser)
|
19
|
+
(
|
20
|
+
parser.add_argument(
|
21
|
+
"-b", "--batch", "--noinput",
|
22
|
+
action="store_false",
|
23
|
+
dest="interactive",
|
24
|
+
default=True,
|
25
|
+
help="Do not prompt for input of any kind.",
|
26
|
+
),
|
27
|
+
)
|
28
|
+
|
29
|
+
def runcmd(self, cmd, **options):
|
30
|
+
bashopts = dict(shell=True, universal_newlines=True)
|
31
|
+
cp = subprocess.run(cmd, **bashopts)
|
32
|
+
if cp.returncode != 0:
|
33
|
+
# subprocess.run("sudo journalctl -xe", **kw)
|
34
|
+
raise CommandError(f"{cmd} ended with return code {cp.returncode}")
|
35
|
+
|
36
|
+
def handle(self, *args, **options):
|
37
|
+
if len(args) > 0:
|
38
|
+
raise CommandError("This command takes no arguments (got %r)" % args)
|
39
|
+
|
40
|
+
using = options.get("database", DEFAULT_DB_ALIAS)
|
41
|
+
dbname = settings.DATABASES[using]["NAME"]
|
42
|
+
interactive = options.get("interactive")
|
43
|
+
if interactive:
|
44
|
+
msg = f"WARNING: running this test can break your database ({dbname})."
|
45
|
+
msg += "\nAre you sure?"
|
46
|
+
if not confirm(msg, default=True):
|
47
|
+
raise CommandError("User abort.")
|
48
|
+
|
49
|
+
kwargs = dict()
|
50
|
+
kwargs["interactive"] = False
|
51
|
+
kwargs["verbosity"] = options.get("verbosity")
|
52
|
+
tmpdir = settings.SITE.site_dir / "tmp"
|
53
|
+
# call_command("prep", '--keepmedia', **kwargs)
|
54
|
+
call_command("dump2py", '-o', tmpdir / "a", **kwargs)
|
55
|
+
self.runcmd(f"python manage.py run {str(tmpdir / 'a' / 'restore.py')} -b")
|
56
|
+
call_command("dump2py", '-o', tmpdir / "b", **kwargs)
|
57
|
+
self.runcmd(f"diff {tmpdir / 'a'} {tmpdir / 'b'}")
|
58
|
+
print(f"Successfully ran double-dump test in {tmpdir}.")
|
59
|
+
# print(f"The double-dump test was successful, we can remove {tmpdir}.")
|
60
|
+
# shutil.rmtree(tmpdir)
|
@@ -300,7 +300,7 @@ def bv2kw(fieldname, values):
|
|
300
300
|
"""
|
301
301
|
|
302
302
|
def main(args):
|
303
|
-
loader = DpyLoader(globals(), quick=args.quick)
|
303
|
+
loader = DpyLoader(globals(), quick=args.quick, strict=True)
|
304
304
|
from django.core.management import call_command
|
305
305
|
call_command('initdb', interactive=args.interactive)
|
306
306
|
os.chdir(os.path.dirname(__file__))
|
@@ -449,35 +449,23 @@ if __name__ == '__main__':
|
|
449
449
|
hope = True
|
450
450
|
break
|
451
451
|
|
452
|
-
|
453
|
-
# ~ for d in deps:
|
454
|
-
# ~ if d in unsorted:
|
455
|
-
# ~ ok = False
|
456
|
-
# ~ if ok:
|
457
|
-
# ~ sorted.append(model)
|
458
|
-
# ~ unsorted.remove(model)
|
459
|
-
# ~ hope = True
|
460
|
-
# ~ break
|
461
|
-
# ~ else:
|
462
|
-
# ~ guilty[model] = deps
|
463
|
-
# ~ print model.__name__, "depends on", [m.__name__ for m in deps]
|
464
|
-
if unsorted:
|
452
|
+
if len(unsorted):
|
465
453
|
assert len(unsorted) == len(guilty)
|
466
|
-
|
454
|
+
todo = [(m, m.objects.count(), guilty[m]) for m in unsorted]
|
455
|
+
todo.sort(key=lambda i: i[1])
|
456
|
+
|
457
|
+
msg = "There are %d models with circular dependencies :\n" % len(todo)
|
467
458
|
msg += "- " + "\n- ".join(
|
468
459
|
[
|
469
460
|
full_model_name(m)
|
470
|
-
+ " (depends on %s)" %
|
471
|
-
|
472
|
-
|
473
|
-
)
|
474
|
-
|
475
|
-
#
|
476
|
-
# in random order which would cause false ddt to fail
|
477
|
-
for ln in msg.splitlines():
|
478
|
-
self.stream.write("\n# %s" % ln)
|
461
|
+
+ " (%d rows, depends on %s)" % (
|
462
|
+
count, ", ".join([full_model_name(d) for d in deps])
|
463
|
+
) for m, count, deps in todo]
|
464
|
+
) + "\n\n"
|
465
|
+
for ln in msg.splitlines():
|
466
|
+
self.stream.write("\n# %s" % ln)
|
479
467
|
logger.info(msg)
|
480
|
-
sorted.extend(
|
468
|
+
sorted.extend([i[0] for i in todo])
|
481
469
|
return sorted
|
482
470
|
|
483
471
|
def value2string(self, obj, field):
|
lino/management/commands/prep.py
CHANGED
lino/modlib/comments/mixins.py
CHANGED
@@ -123,10 +123,12 @@ class Commentable(MemoReferrable):
|
|
123
123
|
return _("Created new {model} {obj}.").format(
|
124
124
|
model=self.__class__._meta.verbose_name, obj=self)
|
125
125
|
|
126
|
-
@classmethod
|
127
|
-
def add_comments_filter(cls, qs, ar):
|
128
|
-
|
126
|
+
# @classmethod
|
127
|
+
# def add_comments_filter(cls, qs, ar):
|
128
|
+
# return qs
|
129
129
|
|
130
|
-
def
|
131
|
-
|
132
|
-
|
130
|
+
def get_comment_group(self):
|
131
|
+
pass
|
132
|
+
|
133
|
+
def on_create_comment(self, comment, ar):
|
134
|
+
comment.private = dd.plugins.comments.private_default
|
lino/modlib/comments/models.py
CHANGED
@@ -9,7 +9,7 @@ from django.db import models
|
|
9
9
|
from django.db.models import Q
|
10
10
|
from django.core import validators
|
11
11
|
from django.utils.html import mark_safe, format_html, SafeString
|
12
|
-
from django.
|
12
|
+
from django.utils.translation import ngettext
|
13
13
|
from django.conf import settings
|
14
14
|
# from lino.utils.html import E, tostring, fromstring
|
15
15
|
|
@@ -24,6 +24,7 @@ from lino.modlib.search.mixins import ElasticSearchable
|
|
24
24
|
from lino.modlib.gfks.mixins import Controllable
|
25
25
|
from lino.modlib.memo.mixins import Previewable, MemoReferrable
|
26
26
|
from lino.modlib.publisher.mixins import Publishable
|
27
|
+
from lino_xl.lib.groups.mixins import Groupwise
|
27
28
|
from .choicelists import CommentEvents, Emotions
|
28
29
|
from .mixins import Commentable, MyEmotionField
|
29
30
|
from .roles import CommentsReader, CommentsStaff
|
@@ -49,8 +50,9 @@ class Comment(
|
|
49
50
|
Publishable,
|
50
51
|
DateRangeObservable,
|
51
52
|
MemoReferrable,
|
53
|
+
Groupwise
|
52
54
|
):
|
53
|
-
class Meta
|
55
|
+
class Meta:
|
54
56
|
app_label = "comments"
|
55
57
|
abstract = dd.is_abstract_model(__name__, "Comment")
|
56
58
|
verbose_name = _("Comment")
|
@@ -119,8 +121,7 @@ class Comment(
|
|
119
121
|
# more_text = dd.RichTextField(_("More text"), blank=True)
|
120
122
|
|
121
123
|
private = models.BooleanField(
|
122
|
-
_("Confidential"), default=dd.plugins.comments.private_default
|
123
|
-
)
|
124
|
+
_("Confidential"), default=dd.plugins.comments.private_default)
|
124
125
|
|
125
126
|
comment_type = dd.ForeignKey("comments.CommentType", blank=True, null=True)
|
126
127
|
# reply_vote = models.BooleanField(_("Upvote"), null=True, blank=True)
|
@@ -187,10 +188,10 @@ class Comment(
|
|
187
188
|
# if o.emotion.button_text:
|
188
189
|
# yield o.emotion.button_text
|
189
190
|
# yield " "
|
190
|
-
if False:
|
191
|
-
|
192
|
-
else:
|
193
|
-
|
191
|
+
# if False:
|
192
|
+
# attrs = {"class": "bordertext"}
|
193
|
+
# else:
|
194
|
+
# attrs = {}
|
194
195
|
s += ar.obj2htmls(o, naturaltime(o.created), title=t)
|
195
196
|
s += format_html(" {} ", _("by"))
|
196
197
|
if o.user is None:
|
@@ -284,7 +285,11 @@ class Comment(
|
|
284
285
|
def on_create(self, ar):
|
285
286
|
super().on_create(ar)
|
286
287
|
if self.owner_id:
|
287
|
-
self.
|
288
|
+
self.owner.on_create_comment(self, ar)
|
289
|
+
|
290
|
+
def get_default_group(self):
|
291
|
+
if self.owner_id:
|
292
|
+
return self.owner.get_comment_group()
|
288
293
|
|
289
294
|
def after_ui_save(self, ar, cw):
|
290
295
|
super().after_ui_save(ar, cw)
|
@@ -355,9 +360,10 @@ class Comment(
|
|
355
360
|
qs = qs.filter(private=False)
|
356
361
|
elif not user.user_type.has_required_roles([CommentsStaff]):
|
357
362
|
qs = qs.filter(Q(private=False) | Q(user=user))
|
358
|
-
|
359
|
-
|
360
|
-
|
363
|
+
|
364
|
+
# sar = BaseRequest(user=ar.get_user())
|
365
|
+
# for m in rt.models_by_base(Commentable):
|
366
|
+
# qs = m.add_comments_filter(qs, sar)
|
361
367
|
|
362
368
|
qs = qs.annotate(num_replies=models.Count("replies_to_this"))
|
363
369
|
qs = qs.annotate(num_reactions=models.Count("reactions_to_this"))
|
lino/modlib/comments/ui.py
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
# License: GNU Affero General Public License v3 (see file COPYING for details)
|
4
4
|
|
5
5
|
from lino.modlib.publisher.choicelists import PageFillers
|
6
|
-
from django.utils.translation import ngettext
|
7
6
|
from django.contrib.humanize.templatetags.humanize import naturaltime
|
8
7
|
from django.contrib.contenttypes.models import ContentType
|
9
8
|
from django.db import models
|
@@ -73,7 +72,7 @@ class CommentDetail(dd.DetailLayout):
|
|
73
72
|
)
|
74
73
|
|
75
74
|
more2 = """
|
76
|
-
id user
|
75
|
+
id user group
|
77
76
|
owner_type owner_id
|
78
77
|
created modified
|
79
78
|
comment_type
|
lino/modlib/memo/mixins.py
CHANGED
@@ -214,6 +214,8 @@ class Previewable(BasePreviewable):
|
|
214
214
|
body_short_preview = RichTextField(_("Preview"), blank=True, editable=False)
|
215
215
|
body_full_preview = RichTextField(_("Preview (full)"), blank=True, editable=False)
|
216
216
|
|
217
|
+
edit_body = dd.ShowEditor("body")
|
218
|
+
|
217
219
|
def get_body_parsed(self, ar, short=False):
|
218
220
|
if ar.renderer is settings.SITE.kernel.editing_front_end.renderer:
|
219
221
|
return self.body_short_preview if short else self.body_full_preview
|
lino/modlib/publisher/models.py
CHANGED
@@ -38,6 +38,7 @@ from lino.modlib.linod.choicelists import schedule_daily
|
|
38
38
|
from lino.modlib.memo.mixins import Previewable
|
39
39
|
from lino.mixins.polymorphic import Polymorphic
|
40
40
|
from lino_xl.lib.topics.mixins import Taggable
|
41
|
+
from lino_xl.lib.groups.mixins import Groupwise
|
41
42
|
# from .utils import render_node
|
42
43
|
|
43
44
|
from lino.api import rt, dd
|
@@ -50,19 +51,22 @@ from .ui import *
|
|
50
51
|
|
51
52
|
|
52
53
|
class Page(
|
53
|
-
Hierarchical, Sequenced, Previewable, Commentable, PublishableContent,
|
54
|
+
Hierarchical, Sequenced, Previewable, Commentable, PublishableContent,
|
55
|
+
Taggable # , Groupwise
|
54
56
|
):
|
55
57
|
class Meta:
|
56
58
|
verbose_name = _("Page")
|
57
59
|
verbose_name_plural = _("Pages")
|
58
60
|
abstract = dd.is_abstract_model(__name__, "Page")
|
61
|
+
# if dd.is_installed("groups"):
|
62
|
+
# unique_together = ["group", "ref", "language"]
|
63
|
+
# else:
|
64
|
+
# unique_together = ["ref", "language"]
|
59
65
|
unique_together = ["ref", "language"]
|
60
66
|
|
61
67
|
memo_command = "page"
|
62
68
|
|
63
|
-
ref =
|
64
|
-
_("Reference"), max_length=200, blank=True, null=True)
|
65
|
-
|
69
|
+
ref = dd.CharField(_("Reference"), max_length=200, blank=True, null=True)
|
66
70
|
title = dd.CharField(_("Title"), max_length=250, blank=True)
|
67
71
|
child_node_depth = models.IntegerField(default=1)
|
68
72
|
# page_type = PageTypes.field(blank=True, null=True)
|
@@ -362,10 +366,14 @@ class Page(
|
|
362
366
|
# return True
|
363
367
|
|
364
368
|
def get_absolute_url(self, **kwargs):
|
369
|
+
parts = []
|
370
|
+
if self.group is not None:
|
371
|
+
if self.group.ref is not None:
|
372
|
+
parts.append(self.group.ref)
|
365
373
|
if self.ref:
|
366
374
|
if self.ref != "index":
|
367
|
-
|
368
|
-
return dd.plugins.publisher.build_plain_url(**kwargs)
|
375
|
+
parts.append(self.group.ref)
|
376
|
+
return dd.plugins.publisher.build_plain_url(*parts, **kwargs)
|
369
377
|
|
370
378
|
def get_publisher_response(self, ar):
|
371
379
|
if ar and ar.request and self.language != ar.request.LANGUAGE_CODE:
|
@@ -382,8 +390,7 @@ class Page(
|
|
382
390
|
sources.add(p.id)
|
383
391
|
p = p.translated_from
|
384
392
|
qs = self.__class__.objects.filter(
|
385
|
-
language=rqlang, translated_from__in=sources
|
386
|
-
)
|
393
|
+
language=rqlang, translated_from__in=sources)
|
387
394
|
obj = qs.first()
|
388
395
|
# obj = self.translated_to.filter(language=rqlang).first()
|
389
396
|
# print("20231027 redirect to translation", tt.language, ar.request.LANGUAGE_CODE)
|
lino/utils/cycler.py
CHANGED
@@ -51,14 +51,17 @@ class Cycler(object):
|
|
51
51
|
return item.pop()
|
52
52
|
return item
|
53
53
|
|
54
|
+
def reset(self):
|
55
|
+
self.current = 0
|
56
|
+
|
54
57
|
def __len__(self):
|
55
58
|
return len(self.items)
|
56
59
|
|
60
|
+
def __iter__(self):
|
61
|
+
return self.items.__iter__()
|
62
|
+
|
57
63
|
def __repr__(self):
|
58
64
|
return f"Cycler({self.current} of {len(self.items)} in loop {self.loop_no})"
|
59
65
|
|
60
|
-
def reset(self):
|
61
|
-
self.current = 0
|
62
|
-
|
63
66
|
def __getitem__(self, *args):
|
64
67
|
return self.items.__getitem__(*args)
|
lino/utils/dpy.py
CHANGED
@@ -6,30 +6,27 @@ Defines Lino's **Python serializer and deserializer**. See
|
|
6
6
|
:doc:`Specification </specs/dpy>`.
|
7
7
|
"""
|
8
8
|
|
9
|
-
|
10
|
-
from packaging.version import Version
|
11
|
-
|
9
|
+
# import traceback
|
12
10
|
import os
|
13
11
|
import importlib.util
|
14
12
|
import sys
|
15
|
-
from
|
13
|
+
from packaging.version import Version
|
14
|
+
from pathlib import Path
|
16
15
|
# from lino import AFTER17
|
17
|
-
|
18
16
|
from django.conf import settings
|
19
17
|
from django.db import models
|
20
|
-
|
21
18
|
from django.utils import translation
|
22
19
|
from django.utils.module_loading import import_string
|
23
20
|
from django.utils.encoding import force_str
|
24
|
-
|
25
21
|
# from django.db import IntegrityError
|
26
22
|
from django.core.serializers import base
|
27
23
|
from django.core.exceptions import ValidationError
|
28
24
|
# from django.core.exceptions import ObjectDoesNotExist
|
29
|
-
|
30
25
|
# from lino.utils.mldbc.fields import BabelCharField, BabelTextField
|
31
26
|
# from lino.core.choicelists import ChoiceListField
|
32
27
|
from lino.core.utils import obj2str, full_model_name
|
28
|
+
from lino.core.fields import VirtualField
|
29
|
+
from lino import logger
|
33
30
|
|
34
31
|
SUFFIX = ".py"
|
35
32
|
|
@@ -118,7 +115,7 @@ if SUPPORT_EMPTY_FIXTURES:
|
|
118
115
|
class FakeDeserializedObject(base.DeserializedObject):
|
119
116
|
"""Imitates DeserializedObject required by loaddata.
|
120
117
|
|
121
|
-
Unlike normal DeserializedObject, we *
|
118
|
+
Unlike normal DeserializedObject, we do *not* want to bypass
|
122
119
|
pre_save and validation methods on the individual objects.
|
123
120
|
|
124
121
|
"""
|
@@ -126,7 +123,6 @@ class FakeDeserializedObject(base.DeserializedObject):
|
|
126
123
|
def __init__(self, deserializer, object, **kw):
|
127
124
|
super().__init__(object, deserializer, **kw)
|
128
125
|
self.object = object
|
129
|
-
# self.name = name
|
130
126
|
self.deserializer = deserializer
|
131
127
|
|
132
128
|
def save(self, *args, **kw):
|
@@ -134,16 +130,9 @@ class FakeDeserializedObject(base.DeserializedObject):
|
|
134
130
|
# logger.info("Loading %s...",self.name)
|
135
131
|
|
136
132
|
self.try_save(*args, **kw)
|
137
|
-
# if self.try_save(*args,**kw):
|
138
|
-
# self.deserializer.saved += 1
|
139
|
-
# else:
|
140
|
-
# self.deserializer.save_later.append(self)
|
141
133
|
|
142
134
|
def try_save(self, *args, **kw):
|
143
|
-
"""Try to save
|
144
|
-
on success, `False` if this instance wasn't saved and should be
|
145
|
-
deferred.
|
146
|
-
"""
|
135
|
+
"""Try to save this Model instance."""
|
147
136
|
obj = self.object
|
148
137
|
try:
|
149
138
|
m = getattr(obj, "before_dumpy_save", None)
|
@@ -152,39 +141,56 @@ class FakeDeserializedObject(base.DeserializedObject):
|
|
152
141
|
if not self.deserializer.quick:
|
153
142
|
try:
|
154
143
|
obj.full_clean()
|
155
|
-
except ValidationError
|
144
|
+
except ValidationError:
|
156
145
|
# raise Exception("{0} : {1}".format(obj2str(obj), e))
|
157
146
|
raise # Exception("{0} : {1}".format(obj2str(obj), e))
|
158
147
|
obj.save(*args, **kw)
|
159
|
-
logger.debug("%s has been saved"
|
148
|
+
logger.debug("%s has been saved", obj2str(obj))
|
160
149
|
self.deserializer.register_success()
|
161
|
-
return True
|
162
|
-
# except ValidationError,e:
|
163
|
-
# except ObjectDoesNotExist,e:
|
164
|
-
# except (ValidationError,ObjectDoesNotExist), e:
|
165
|
-
# except (ValidationError,ObjectDoesNotExist,IntegrityError), e:
|
166
150
|
except Exception as e:
|
167
|
-
if
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
"Failed to save %s from manual fixture:" % obj2str(obj)
|
173
|
-
)
|
174
|
-
raise
|
151
|
+
if not settings.SITE.loading_from_dump:
|
152
|
+
# hand-written fixtures are expected to yield in savable order
|
153
|
+
logger.warning(
|
154
|
+
"Failed to save %s from manual fixture:", obj2str(obj))
|
155
|
+
raise
|
175
156
|
deps = [
|
176
|
-
f.
|
177
|
-
|
178
|
-
if f.remote_field and f.remote_field.model
|
179
|
-
]
|
157
|
+
f for f in obj._meta.fields
|
158
|
+
if f.remote_field and f.remote_field.model]
|
180
159
|
if not deps:
|
181
|
-
logger.exception(e)
|
182
|
-
|
160
|
+
# logger.exception(e)
|
161
|
+
msg = f"Failed to save independent {obj2str(obj)}."
|
162
|
+
raise Exception(msg) from e
|
163
|
+
self.on_restore_failure(obj)
|
183
164
|
self.deserializer.register_failure(self, e)
|
184
|
-
|
185
|
-
|
186
|
-
#
|
187
|
-
#
|
165
|
+
|
166
|
+
def on_restore_failure(self, obj):
|
167
|
+
# When a database row fails to save during restore.py, Lino looks
|
168
|
+
# whether it has nullable FK fields that aren't null, set these to None
|
169
|
+
# and try to save an intermediate row.
|
170
|
+
|
171
|
+
nullable = dict()
|
172
|
+
# required = dict()
|
173
|
+
for f in obj._meta.fields:
|
174
|
+
if f.remote_field and f.remote_field.model:
|
175
|
+
if f.null and (v := f.value_from_object(obj)) is not None:
|
176
|
+
nullable[f] = v
|
177
|
+
if not (nullable):
|
178
|
+
return
|
179
|
+
for f in nullable.keys():
|
180
|
+
setattr(obj, f.attname, None)
|
181
|
+
# f.set_value_in_object(obj, None)
|
182
|
+
try:
|
183
|
+
obj.full_clean()
|
184
|
+
obj.save()
|
185
|
+
except Exception:
|
186
|
+
pass
|
187
|
+
for f, v in nullable.items():
|
188
|
+
setattr(obj, f.attname, v)
|
189
|
+
# f.set_value_in_object(obj, v)
|
190
|
+
logger.info(
|
191
|
+
"Save intermediate %s without %s",
|
192
|
+
obj.__class__.__name__,
|
193
|
+
", ".join([f"{f.name}={v}" for f, v in nullable.items()]))
|
188
194
|
|
189
195
|
|
190
196
|
class Serializer(base.Serializer):
|
@@ -205,7 +211,7 @@ class Serializer(base.Serializer):
|
|
205
211
|
raise NotImplementedError("Don't use dumpdata but `dump2py`")
|
206
212
|
|
207
213
|
|
208
|
-
class FlushDeferredObjects
|
214
|
+
class FlushDeferredObjects:
|
209
215
|
"""
|
210
216
|
Indicator class object.
|
211
217
|
Fixture may yield a `FlushDeferredObjects`
|
@@ -215,10 +221,11 @@ class FlushDeferredObjects(object):
|
|
215
221
|
pass
|
216
222
|
|
217
223
|
|
218
|
-
class LoaderBase
|
224
|
+
class LoaderBase:
|
219
225
|
quick = False
|
220
226
|
source_version = None
|
221
|
-
max_deferred_objects =
|
227
|
+
max_deferred_objects = 4000
|
228
|
+
strict = False
|
222
229
|
|
223
230
|
def __init__(self):
|
224
231
|
# logger.info("20120225 DpyLoader.__init__()")
|
@@ -282,13 +289,13 @@ class LoaderBase(object):
|
|
282
289
|
self.flush_deferred_objects()
|
283
290
|
if count > self.max_deferred_objects + 1:
|
284
291
|
raise Exception(
|
285
|
-
"More than {} deferred objects"
|
286
|
-
)
|
292
|
+
f"More than {self.max_deferred_objects} deferred objects")
|
287
293
|
l.append(obj)
|
288
294
|
# report a full traceback, but only once per model and
|
289
295
|
# exception type:
|
290
296
|
k = (obj.object.__class__, e.__class__)
|
291
297
|
if k not in self.reported_tracebacks:
|
298
|
+
# traceback.print_exc(e)
|
292
299
|
logger.exception(e)
|
293
300
|
self.reported_tracebacks.add(k)
|
294
301
|
|
@@ -333,14 +340,17 @@ class LoaderBase(object):
|
|
333
340
|
", ".join([str(o.object.pk) for o in objects]),
|
334
341
|
)
|
335
342
|
count += len(objects)
|
336
|
-
msg = "Abandoning with {} unsaved instances:{}"
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
#
|
342
|
-
#
|
343
|
-
#
|
343
|
+
msg = f"Abandoning with {count} unsaved instances:{s}"
|
344
|
+
if self.strict:
|
345
|
+
raise Exception(msg)
|
346
|
+
logger.warning(msg)
|
347
|
+
|
348
|
+
# Until 20250730: Don't raise an exception. The unsaved instances
|
349
|
+
# got lost and the loaddata should be done again, but meanwhile the
|
350
|
+
# database is not necessarily invalid and may be used for further
|
351
|
+
# testing. And anyway, loaddata would catch it and still continue.
|
352
|
+
# Since 20250730: But we need a way to make sure that the restore.py
|
353
|
+
# worked well.
|
344
354
|
|
345
355
|
settings.SITE.loading_from_dump = False
|
346
356
|
# reset to False because the same SITE might get reused by
|
@@ -350,11 +360,12 @@ class LoaderBase(object):
|
|
350
360
|
class DpyLoader(LoaderBase):
|
351
361
|
"""Instantiated by :xfile:`restore.py`."""
|
352
362
|
|
353
|
-
def __init__(self, globals_dict, quick=None):
|
363
|
+
def __init__(self, globals_dict, quick=None, strict=True):
|
354
364
|
if quick is not None:
|
355
365
|
self.quick = quick
|
356
366
|
self.globals_dict = globals_dict
|
357
|
-
|
367
|
+
self.strict = strict
|
368
|
+
super().__init__()
|
358
369
|
self.source_version = globals_dict["SOURCE_VERSION"]
|
359
370
|
site = globals_dict["settings"].SITE
|
360
371
|
site.startup()
|
@@ -369,7 +380,7 @@ class DpyDeserializer(LoaderBase):
|
|
369
380
|
"""The Django deserializer for :ref:`dpy`.
|
370
381
|
|
371
382
|
Note that this deserializer explicitly ignores fixtures whose
|
372
|
-
source file is located in the current directory because
|
383
|
+
source file is located in the current directory because in the case
|
373
384
|
of `.py` files this can lead to side effects when importing them.
|
374
385
|
See e.g. :ticket:`1029`. We consider it an odd behaviour of
|
375
386
|
Django to search for fixtures also in the current directory (and
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: lino
|
3
|
-
Version: 25.
|
3
|
+
Version: 25.8.1
|
4
4
|
Summary: A framework for writing desktop-like web applications using Django and ExtJS or React
|
5
5
|
Project-URL: Homepage, https://www.lino-framework.org
|
6
6
|
Project-URL: Repository, https://gitlab.com/lino-framework/lino
|
@@ -1,10 +1,10 @@
|
|
1
1
|
lino/.cvsignore,sha256=1vrrWoP-WD8hPfCszHHIiJEi8KUMRCt5WvoKB9TSB1k,28
|
2
2
|
lino/SciTEDirectory.properties,sha256=rCYi_e-6h8Yx5DwXhAa6MBPlVINcl6Vv9BQDYZV2_go,28
|
3
|
-
lino/__init__.py,sha256=
|
3
|
+
lino/__init__.py,sha256=SAUG9WJQxlJPY6VB5ga-HE3k5vefql6UsZTQk7kre7E,6176
|
4
4
|
lino/ad.py,sha256=AQ-vJ4scac1mx3xegXezxnxyOQpV-a0q3VFMJSDbj2s,142
|
5
5
|
lino/apps.py,sha256=ECq-dPARDkuhngwNrcipse3b4Irj70HxJs44uWEZFc4,27
|
6
6
|
lino/hello.py,sha256=7-PJg7PnEiznyETqGjOwXcKh8rda0qLetpbS2gvRYy0,532
|
7
|
-
lino/help_texts.py,sha256=
|
7
|
+
lino/help_texts.py,sha256=RPw9EDHoQD7foqFTXNC8gWlnaWPqTZJQYgBTzNtDqIg,92385
|
8
8
|
lino/api/__init__.py,sha256=WmzHU-rHdZ68se_nI0SmepQTGE8-cd9tPpovHRH9aag,512
|
9
9
|
lino/api/ad.py,sha256=F6SrcKPRRalHKOZ7QLwsRWGq9hhykQIeo0b85cEk9NQ,314
|
10
10
|
lino/api/dd.py,sha256=w-5qIpWdXu5yXyCC4Qh4ApB3yZTn0AwdxAjEhH8PyVE,7510
|
@@ -28,7 +28,7 @@ lino/config/unused/403.html,sha256=ePwDIUXhz1iD8xXWWyt5xEvpcGIHU4LMnXma8x0ik1c,2
|
|
28
28
|
lino/config/unused/404.html,sha256=GOJrAyF6NcM69ETdSHgjff_-lvYs_-bOYhyZBem7x3I,220
|
29
29
|
lino/config/unused/500.html,sha256=aWmP37uPoMS-PJgPuBloxdx0nEreU7AvkXxsex3yVYs,544
|
30
30
|
lino/core/__init__.py,sha256=I4X69XK6Y1MZ8X6tC13Wmg13C3r5iTfYcFDiPJKpUdw,726
|
31
|
-
lino/core/actions.py,sha256=
|
31
|
+
lino/core/actions.py,sha256=0ebutR3pIMXa7cNpoVJlyt6TueWrfJNNygvQL7XkOgQ,32659
|
32
32
|
lino/core/actors.py,sha256=hbbzTpP7iBjQ2Eup4JhV5zx8w64arqPpkv4RnCptyKY,72585
|
33
33
|
lino/core/atomizer.py,sha256=yK_l9-g8RZIjy2_iBB_efpyO1CrvegCBbjobD5mTTVc,13476
|
34
34
|
lino/core/boundaction.py,sha256=06NNPjCesEr-R1YQKkiuy8JKzDrMJJ948x9jczOkZqY,7850
|
@@ -37,7 +37,7 @@ lino/core/choicelists.py,sha256=5Xu3M5ZVOis2JoNSuNiJGBHdkqCwLofUxSd19iLIlKs,3650
|
|
37
37
|
lino/core/classproperty.py,sha256=_E95WPAs7BWbAuFpPvoYM2ZwW_mbq3rvF7o43WsMq_8,4316
|
38
38
|
lino/core/constants.py,sha256=GwSyViDk3wClZzgbrCo6N-JYOa3LCP46FSof-iZ5RRU,5085
|
39
39
|
lino/core/dashboard.py,sha256=kKUoZ1P6He0By3qUOkNIPAVF1zPkXxGFgHsCOdQ8syM,6672
|
40
|
-
lino/core/dbtables.py,sha256=
|
40
|
+
lino/core/dbtables.py,sha256=b3D7gNaLpNvRgiFmOcfIwnW14Z4eCT0nWaPE8OjziOU,29258
|
41
41
|
lino/core/dbutils.py,sha256=_QHcWd-ajLUwt5G8uOp8d47lZQKD3VseHnqKJke18rA,263
|
42
42
|
lino/core/ddh.py,sha256=dYScxWKTOCDEgow7wJNJe812ESasmmITPK2ovraBQno,3172
|
43
43
|
lino/core/diff.py,sha256=XQ-oQQDS_v3kXd4eRP9Hwr5UCgp-TPZIPVav9ZblUno,5882
|
@@ -60,7 +60,7 @@ lino/core/renderer.py,sha256=HhRC_gtrapNLw2Xl-cs67YdI_NdEdJ2ULsbvs5gb2wA,48252
|
|
60
60
|
lino/core/requests.py,sha256=_kwmx8VAwdaPssIdsZt_GzCst67BTR7WwVv4z_KCsNo,96389
|
61
61
|
lino/core/roles.py,sha256=PXwk436xUupxdbJcygRSYFu7ixfKjAJPQRUQ8sy0lB0,4425
|
62
62
|
lino/core/signals.py,sha256=ORY2s3Krlh9n24XyHetfwbeUhCqzib6YSqWeFTTY7ps,979
|
63
|
-
lino/core/site.py,sha256=
|
63
|
+
lino/core/site.py,sha256=DiKF-yRzubEE7xpBntBh4LGlbIaOs6jNd2dT1uTh9dg,83401
|
64
64
|
lino/core/store.py,sha256=bbWrbsV-hPS1FF3e2ABiQHQ_Q61tD2XSRBDSwoTW6jQ,46093
|
65
65
|
lino/core/tables.py,sha256=sjmVu3A8gnamxwy16n76UJy2BXgiqeNeCEbI4oGd93Q,25431
|
66
66
|
lino/core/urls.py,sha256=06QlmN1vpxjmb5snO3SPpP6lX1pMdE60bTiBiC77_vQ,2677
|
@@ -98,9 +98,10 @@ lino/locale/zh_Hant/LC_MESSAGES/django.po,sha256=t4Sjt5ygmJzPhmf5FqEgQmRKoFErB0R
|
|
98
98
|
lino/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
99
99
|
lino/management/commands/__init__.py,sha256=raVDRiXns3SegyXEhaLZMcxfEDs7ggy2nFUN5D0f5F0,47
|
100
100
|
lino/management/commands/buildcache.py,sha256=MrOio6uBtQNW5gnbRrPIjarwUe18R-irxC7dLBBCpR4,1949
|
101
|
+
lino/management/commands/ddt.py,sha256=bn9DqZCztwJooa8u9lDBT96sB6o8on1aJWAUKafoQT8,2347
|
101
102
|
lino/management/commands/demotest.py,sha256=NCUr3ttqU0oorOIc3Z6R_cNNgw5_OzHDbQf0HWlcB1g,4877
|
102
103
|
lino/management/commands/diag.py,sha256=vt-NlZUx5gf7T4EpbM-gle3tAwMuwfPQY8lUgxjFaUw,478
|
103
|
-
lino/management/commands/dump2py.py,sha256=
|
104
|
+
lino/management/commands/dump2py.py,sha256=V2weGrZnb2JzqNuJrmLWnkeVJFVHRbA-6f7b4f7cPBk,19895
|
104
105
|
lino/management/commands/dump_settings.py,sha256=tGOR4h_ueVe2DOk84ILFvzvndt0doshvaxRkybB-rnY,2009
|
105
106
|
lino/management/commands/initdb.py,sha256=j_cWTlugP2-UP_qnHpXmz_pzPGRK1oORymjxphhcdEg,11398
|
106
107
|
lino/management/commands/install.py,sha256=k7lCJznVLS6swR4ayKNulj5TPpJ3qf99Fha87TZAaUk,1889
|
@@ -109,7 +110,7 @@ lino/management/commands/makescreenshots.py,sha256=fJF7ATZz7_s1IWkkYMEHVTLB5r5C1
|
|
109
110
|
lino/management/commands/makeui.py,sha256=qYz68fnUKNXicZfGy3GXdjIZubhg1KyQnqjMblFN_LY,4813
|
110
111
|
lino/management/commands/mergedata.py,sha256=-dPvBtyc-AqKpQeL4TUd2IKHGe8EaaW8Citcsp_hwEU,2527
|
111
112
|
lino/management/commands/passwd.py,sha256=S-7qyUWOaBnqRo_quwWewJjzwonqK4PBP2j8fuBGy3c,3012
|
112
|
-
lino/management/commands/prep.py,sha256=
|
113
|
+
lino/management/commands/prep.py,sha256=dsHu4owaAJB3kIlRLh-11WN3mMLR3I2EpEYOxBWJGfM,2156
|
113
114
|
lino/management/commands/qtclient.py,sha256=ltZyz-SmIevotRmv26TmPiW8VTXKK37DUwdwwMUzrHI,4715
|
114
115
|
lino/management/commands/resetsequences.py,sha256=v7MdzJCw-rBXljttCsgoMIi-cgubCPptXDlcNO0pCjw,978
|
115
116
|
lino/management/commands/run.py,sha256=MiK53KIACYKDTKF6knJGwU-uzEApGnDxByi-3_nrTjQ,1115
|
@@ -173,10 +174,10 @@ lino/modlib/checkdata/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-
|
|
173
174
|
lino/modlib/checkdata/management/commands/checkdata.py,sha256=z-mvg8R0G7-BiWcyeyeMUoaLEhi9n58lockOtPdmhCg,1962
|
174
175
|
lino/modlib/comments/__init__.py,sha256=XoRLIB-pSsz4EfA2FhsbKQ27fsW_AAvS7SyA0_vhHv8,1678
|
175
176
|
lino/modlib/comments/choicelists.py,sha256=SIA7P_KwtaayqOJxCkwyZouK0Z23-2v4ZFV9a0Zexnk,3314
|
176
|
-
lino/modlib/comments/mixins.py,sha256=
|
177
|
-
lino/modlib/comments/models.py,sha256=
|
177
|
+
lino/modlib/comments/mixins.py,sha256=834JYMsK7qnnqSsQDNSkuHqLw8p4Cu2O7GW3qmVfU5I,4417
|
178
|
+
lino/modlib/comments/models.py,sha256=t_uQeElsbl8X-oaDxwGEIOQZhvvlbgJlImgD7nLIq2s,16090
|
178
179
|
lino/modlib/comments/roles.py,sha256=z3gctvlTa_5PAs-D4pounyzNyuEc31jTFq9g33r6Z1w,751
|
179
|
-
lino/modlib/comments/ui.py,sha256=
|
180
|
+
lino/modlib/comments/ui.py,sha256=u46TU1Qn2jz3CN-pCmhV8D2HXezEhGGSTZjuTJ4GWME,9915
|
180
181
|
lino/modlib/comments/config/comments/comments.js,sha256=7oAnNyx_MKM1iWPu-QSp6iKfnOVdgq7EciQPpxTvYU8,242
|
181
182
|
lino/modlib/comments/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
182
183
|
lino/modlib/comments/fixtures/demo2.py,sha256=DSIqNtGOQlwbg8k43nE2ax1x-H_ifRsozvw6LZM330Y,25407
|
@@ -3541,7 +3542,7 @@ lino/modlib/linod/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
3541
3542
|
lino/modlib/linod/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3542
3543
|
lino/modlib/linod/management/commands/linod.py,sha256=SzJ6-yRpI1QFrXqiS8AO7_c2NmkMdV4JpFhfyGzs5eI,2587
|
3543
3544
|
lino/modlib/memo/__init__.py,sha256=mM4xIODNczmmn43ympj_Un7sPyc9RuIr6zmGKYB5vkk,5381
|
3544
|
-
lino/modlib/memo/mixins.py,sha256=
|
3545
|
+
lino/modlib/memo/mixins.py,sha256=cu5BkaYgNtFtJufqphlJrNJWO_wksZDqMdaQVBSrGAQ,10954
|
3545
3546
|
lino/modlib/memo/models.py,sha256=zUEvWu0dK5QhkU3DeMqNqsrzSUzOl6DLZaJBNytfgrs,4388
|
3546
3547
|
lino/modlib/memo/parser.py,sha256=h21I0EjXfV2oHvekp5LxqtfEGlHREa1eOFHU3yC3sOw,13029
|
3547
3548
|
lino/modlib/memo/views.py,sha256=H3g_nuiMzguptXLSWaWLJClU7BefXPFn9Rh8UIVVBeg,555
|
@@ -3590,7 +3591,7 @@ lino/modlib/printing/config/report/Default.wk.html,sha256=4Ssx2LWm1gVpXf0Q4XoSY1
|
|
3590
3591
|
lino/modlib/publisher/__init__.py,sha256=9w4cclyodBB3PO5rFzheIkzJs-tfA62PSGx2WI0NL5Q,2303
|
3591
3592
|
lino/modlib/publisher/choicelists.py,sha256=gVzjyp1sJ-XewAW-I_bCrKdTLgygLzh1ZwFI1rKyPdo,9070
|
3592
3593
|
lino/modlib/publisher/mixins.py,sha256=yRxAtFSNe9aVvdY-th_a5wmQ76jBfKYWzeNUn-efJMA,6651
|
3593
|
-
lino/modlib/publisher/models.py,sha256=
|
3594
|
+
lino/modlib/publisher/models.py,sha256=BvGv6JxdrKoa1VJITOw5Dlbwj96lRdDkCGcRc3uoxG4,17584
|
3594
3595
|
lino/modlib/publisher/renderer.py,sha256=Rl6fX8PzfX6crmnUh8mdU5Mpe44mSN5lTu_Pv8aVSCk,1845
|
3595
3596
|
lino/modlib/publisher/ui.py,sha256=qWp3JWhO6zN_HSZvSlolmNTgiZgoJeY2_TIDh9nYf3Q,4491
|
3596
3597
|
lino/modlib/publisher/views.py,sha256=l_GomdliB1qCylg7jKKkay3ZgAaOPfWNQQ6ZPDjAUl0,2214
|
@@ -4585,7 +4586,7 @@ lino/utils/ajax.py,sha256=SdMckREfEk6-0oaXZNdEUfi1pL-OwnwS5YzaGbXLjEs,3254
|
|
4585
4586
|
lino/utils/choosers.py,sha256=V3Eo0VtGR0t708DeiSkGwSziVCHkipoe2i95d5wTVYc,18298
|
4586
4587
|
lino/utils/code.py,sha256=-2vc8npHQ5jsrRqzxmjRZZ8FWY7QJ7-sRg7-ZhOsUEU,7053
|
4587
4588
|
lino/utils/config.py,sha256=gtaJURPOwHyV_AW7i7_vin3KTj3UwZa4iviDbldwXGE,8568
|
4588
|
-
lino/utils/cycler.py,sha256=
|
4589
|
+
lino/utils/cycler.py,sha256=Kd4jUUaj181gZxSYWMiZTgF7iT3JXZm57pBh-ZxiOMg,1775
|
4589
4590
|
lino/utils/daemoncommand.py,sha256=NjGShiz09fddIV0WU0jK2nzO_CwPj1MfdmgwAOYZi4M,11525
|
4590
4591
|
lino/utils/dataserializer.py,sha256=-_xHXaGwDSO6-sYEHEa2BtEmKS8bW6gsYx4dV-GbvDs,3779
|
4591
4592
|
lino/utils/dates.py,sha256=eWF5WxA5uJf51Y9PKvDVBWD8yIf6yBF6oO6TeU3ujzw,1030
|
@@ -4594,7 +4595,7 @@ lino/utils/dbhash.py,sha256=tG1IHe6Bz9MaagTI-131gpcLcNw3g642QVvv7GsJH2g,3303
|
|
4594
4595
|
lino/utils/dblogger.py,sha256=kr0YxQY6veymvNg5A4tsvkqW8haRWdwqL0C-_9_QTg0,721
|
4595
4596
|
lino/utils/diag.py,sha256=quiGcv-e0pwn_yjjt7OUMo8kr1qKYmIFBjDnHxr_5X4,18754
|
4596
4597
|
lino/utils/djangotest.py,sha256=Phz1qNp0wDonZRja5dxbCk0Xl3a73gZNiKK8v9tAgZg,8334
|
4597
|
-
lino/utils/dpy.py,sha256=
|
4598
|
+
lino/utils/dpy.py,sha256=pHeOmAQLqn4DLunipn5FwooTttRroqdewgUTUt_Dq5s,21220
|
4598
4599
|
lino/utils/fieldutils.py,sha256=6GwPOfL-Jv-uh5-tZrTqC1hJccqHhdLbVSy4CAeegDA,2957
|
4599
4600
|
lino/utils/format_date.py,sha256=zJu8PO45hGsk6Znq8_93D3vUz9HcY7CjHduAFxoU0v8,3123
|
4600
4601
|
lino/utils/html.py,sha256=nR2h6oa_47Baq5rdSln0aGbqzS6SFsWzl-uqjnGIUWU,3273
|
@@ -4635,8 +4636,8 @@ lino/utils/xml.py,sha256=EGDnO1UaREst9fS7KTESdbHnrrVCwKbRQdvut6B6GmQ,1612
|
|
4635
4636
|
lino/utils/mldbc/__init__.py,sha256=QqWRlzeXaOmFfbCk-vTY3SZMn1-FCf67XnpZdd_Nim0,1134
|
4636
4637
|
lino/utils/mldbc/fields.py,sha256=tAX8G5UKigr9c6g0F3ARIjZZtg406mdaZ--PWSbiH9E,2873
|
4637
4638
|
lino/utils/mldbc/mixins.py,sha256=CkYe5jDa7xp9fJq_V8zcZf8ocxgIjUgHc9KZccvA_Yw,1945
|
4638
|
-
lino-25.
|
4639
|
-
lino-25.
|
4640
|
-
lino-25.
|
4641
|
-
lino-25.
|
4642
|
-
lino-25.
|
4639
|
+
lino-25.8.1.dist-info/METADATA,sha256=S4pvxNfayUCSdOc2fhkj8KdaPN-WvT5pK8PuwHU3Fd4,42534
|
4640
|
+
lino-25.8.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
4641
|
+
lino-25.8.1.dist-info/licenses/AUTHORS.rst,sha256=8VEm_G4HOmYEa4oi1nVoKKsdo4JanekEJCefWd2E8vk,981
|
4642
|
+
lino-25.8.1.dist-info/licenses/COPYING,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
4643
|
+
lino-25.8.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|