morango 0.6.11__py2.py3-none-any.whl → 0.8.6__py2.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.
Files changed (64) hide show
  1. morango/__init__.py +1 -6
  2. morango/api/serializers.py +3 -0
  3. morango/api/viewsets.py +38 -23
  4. morango/apps.py +1 -2
  5. morango/constants/settings.py +3 -0
  6. morango/constants/transfer_stages.py +1 -1
  7. morango/constants/transfer_statuses.py +1 -1
  8. morango/errors.py +4 -0
  9. morango/management/commands/cleanupsyncs.py +64 -14
  10. morango/migrations/0001_initial.py +0 -2
  11. morango/migrations/0001_squashed_0024_auto_20240129_1757.py +583 -0
  12. morango/migrations/0002_auto_20170511_0400.py +0 -2
  13. morango/migrations/0002_store_idx_morango_deserialize.py +21 -0
  14. morango/migrations/0003_auto_20170519_0543.py +0 -2
  15. morango/migrations/0004_auto_20170520_2112.py +0 -2
  16. morango/migrations/0005_auto_20170629_2139.py +0 -2
  17. morango/migrations/0006_instanceidmodel_system_id.py +0 -2
  18. morango/migrations/0007_auto_20171018_1615.py +0 -2
  19. morango/migrations/0008_auto_20171114_2217.py +0 -2
  20. morango/migrations/0009_auto_20171205_0252.py +0 -2
  21. morango/migrations/0010_auto_20171206_1615.py +0 -2
  22. morango/migrations/0011_sharedkey.py +0 -2
  23. morango/migrations/0012_auto_20180927_1658.py +0 -2
  24. morango/migrations/0013_auto_20190627_1513.py +0 -2
  25. morango/migrations/0014_syncsession_extra_fields.py +0 -2
  26. morango/migrations/0015_auto_20200508_2104.py +2 -3
  27. morango/migrations/0016_store_deserialization_error.py +2 -3
  28. morango/migrations/0017_store_last_transfer_session_id.py +1 -2
  29. morango/migrations/0018_auto_20210714_2216.py +2 -3
  30. morango/migrations/0019_auto_20220113_1807.py +2 -3
  31. morango/migrations/0020_postgres_fix_nullable.py +0 -2
  32. morango/migrations/0021_store_partition_index_create.py +0 -2
  33. morango/migrations/0022_rename_instance_fields.py +23 -0
  34. morango/migrations/0023_add_instance_id_fields.py +24 -0
  35. morango/migrations/0024_auto_20240129_1757.py +28 -0
  36. morango/models/__init__.py +0 -6
  37. morango/models/certificates.py +137 -28
  38. morango/models/core.py +48 -46
  39. morango/models/fields/crypto.py +20 -6
  40. morango/models/fields/uuids.py +2 -1
  41. morango/models/utils.py +5 -6
  42. morango/proquint.py +2 -3
  43. morango/registry.py +28 -49
  44. morango/sync/backends/base.py +34 -0
  45. morango/sync/backends/postgres.py +129 -0
  46. morango/sync/backends/utils.py +10 -11
  47. morango/sync/context.py +198 -13
  48. morango/sync/controller.py +33 -11
  49. morango/sync/operations.py +324 -251
  50. morango/sync/session.py +11 -0
  51. morango/sync/syncsession.py +78 -85
  52. morango/sync/utils.py +18 -0
  53. morango/urls.py +3 -3
  54. morango/utils.py +2 -3
  55. {morango-0.6.11.dist-info → morango-0.8.6.dist-info}/METADATA +29 -14
  56. morango-0.8.6.dist-info/RECORD +79 -0
  57. {morango-0.6.11.dist-info → morango-0.8.6.dist-info}/WHEEL +1 -1
  58. morango/models/morango_mptt.py +0 -33
  59. morango/settings.py +0 -115
  60. morango/wsgi.py +0 -33
  61. morango-0.6.11.dist-info/RECORD +0 -77
  62. {morango-0.6.11.dist-info → morango-0.8.6.dist-info/licenses}/AUTHORS.md +0 -0
  63. {morango-0.6.11.dist-info → morango-0.8.6.dist-info/licenses}/LICENSE +0 -0
  64. {morango-0.6.11.dist-info → morango-0.8.6.dist-info}/top_level.txt +0 -0
morango/sync/session.py CHANGED
@@ -1,11 +1,13 @@
1
1
  import logging
2
2
 
3
3
  from requests import exceptions
4
+ from morango import __version__
4
5
  from requests.sessions import Session
5
6
  from requests.utils import super_len
6
7
  from requests.packages.urllib3.util.url import parse_url
7
8
 
8
9
  from morango.utils import serialize_capabilities_to_client_request
10
+ from morango.utils import SETTINGS
9
11
 
10
12
 
11
13
  logger = logging.getLogger(__name__)
@@ -35,6 +37,15 @@ class SessionWrapper(Session):
35
37
  bytes_sent = 0
36
38
  bytes_received = 0
37
39
 
40
+ def __init__(self):
41
+ super(SessionWrapper, self).__init__()
42
+ user_agent_header = "morango/{}".format(__version__)
43
+ if SETTINGS.CUSTOM_INSTANCE_INFO is not None:
44
+ instances = list(SETTINGS.CUSTOM_INSTANCE_INFO)
45
+ if instances:
46
+ user_agent_header += " " + "{}/{}".format(instances[0], SETTINGS.CUSTOM_INSTANCE_INFO.get(instances[0]))
47
+ self.headers["User-Agent"] = "{} {}".format(user_agent_header, self.headers["User-Agent"])
48
+
38
49
  def request(self, method, url, **kwargs):
39
50
  response = None
40
51
  try:
@@ -1,21 +1,20 @@
1
1
  """
2
2
  The main module to be used for initiating the synchronization of data between morango instances.
3
3
  """
4
- import os
5
4
  import json
6
5
  import logging
6
+ import os
7
7
  import socket
8
8
  import uuid
9
9
  from io import BytesIO
10
+ from urllib.parse import urljoin
11
+ from urllib.parse import urlparse
10
12
 
11
13
  from django.utils import timezone
12
- from django.utils.six import iteritems
13
- from django.utils.six import raise_from
14
14
  from requests.adapters import HTTPAdapter
15
15
  from requests.exceptions import HTTPError
16
16
  from requests.packages.urllib3.util.retry import Retry
17
- from django.utils.six.moves.urllib.parse import urljoin
18
- from django.utils.six.moves.urllib.parse import urlparse
17
+ from django.db import transaction, connection
19
18
 
20
19
  from .session import SessionWrapper
21
20
  from morango.api.serializers import CertificateSerializer
@@ -30,16 +29,20 @@ from morango.errors import MorangoError
30
29
  from morango.errors import MorangoResumeSyncError
31
30
  from morango.errors import MorangoServerDoesNotAllowNewCertPush
32
31
  from morango.models.certificates import Certificate
32
+ from morango.models.certificates import Filter
33
33
  from morango.models.certificates import Key
34
34
  from morango.models.core import InstanceIDModel
35
35
  from morango.models.core import SyncSession
36
- from morango.sync.controller import SessionController
36
+ from morango.sync.backends.utils import load_backend
37
+ from morango.sync.context import CompositeSessionContext
37
38
  from morango.sync.context import LocalSessionContext
38
39
  from morango.sync.context import NetworkSessionContext
40
+ from morango.sync.controller import SessionController
39
41
  from morango.sync.utils import SyncSignal
40
42
  from morango.sync.utils import SyncSignalGroup
41
43
  from morango.utils import CAPABILITIES
42
44
  from morango.utils import pid_exists
45
+ from morango.sync.utils import lock_partitions
43
46
 
44
47
  if GZIP_BUFFER_POST in CAPABILITIES:
45
48
  from gzip import GzipFile
@@ -47,6 +50,7 @@ if GZIP_BUFFER_POST in CAPABILITIES:
47
50
 
48
51
  logger = logging.getLogger(__name__)
49
52
 
53
+ DBBackend = load_backend(connection)
50
54
 
51
55
  def _join_with_logical_operator(lst, operator):
52
56
  op = ") {operator} (".format(operator=operator)
@@ -224,6 +228,14 @@ class NetworkSyncConnection(Connection):
224
228
  if not server_cert.verify(message, session_resp.json().get("signature")):
225
229
  raise CertificateSignatureInvalid()
226
230
 
231
+ client_instance = InstanceIDModel.get_or_create_current_instance()[0]
232
+
233
+ server_instance_json = session_resp.json().get("server_instance") or "{}"
234
+ server_instance_id = None
235
+ if server_instance_json:
236
+ server_instance = json.loads(server_instance_json)
237
+ server_instance_id = server_instance.get("id")
238
+
227
239
  # build the data to be used for creating our own syncsession
228
240
  data = {
229
241
  "id": data["id"],
@@ -238,23 +250,29 @@ class NetworkSyncConnection(Connection):
238
250
  "connection_path": self.base_url,
239
251
  "client_ip": data["client_ip"],
240
252
  "server_ip": data["server_ip"],
241
- "client_instance": json.dumps(
253
+ "client_instance_id": client_instance.id,
254
+ "client_instance_json": json.dumps(
242
255
  InstanceIDSerializer(
243
- InstanceIDModel.get_or_create_current_instance()[0]
256
+ client_instance
244
257
  ).data
245
258
  ),
246
- "server_instance": session_resp.json().get("server_instance") or "{}",
259
+ "server_instance_id": server_instance_id,
260
+ "server_instance_json": session_resp.json().get("server_instance") or "{}",
247
261
  "process_id": os.getpid(),
248
262
  }
249
263
  sync_session = SyncSession.objects.create(**data)
250
264
  return SyncSessionClient(self, sync_session)
251
265
 
252
- def resume_sync_session(self, sync_session_id, chunk_size=None):
266
+ def resume_sync_session(self, sync_session_id, chunk_size=None, ignore_existing_process=False):
253
267
  """
254
268
  Resumes an existing sync session given an ID
255
269
 
256
270
  :param sync_session_id: The UUID of the `SyncSession` to resume
257
271
  :param chunk_size: An optional parameter specifying the size for each transferred chunk
272
+ :type chunk_size: int
273
+ :param ignore_existing_process:An optional parameter specifying whether to ignore an
274
+ existing active process ID
275
+ :type ignore_existing_process: bool
258
276
  :return: A SyncSessionClient instance
259
277
  :rtype: SyncSessionClient
260
278
  """
@@ -270,7 +288,8 @@ class NetworkSyncConnection(Connection):
270
288
 
271
289
  # check that process of existing session isn't still running
272
290
  if (
273
- sync_session.process_id
291
+ not ignore_existing_process
292
+ and sync_session.process_id
274
293
  and sync_session.process_id != os.getpid()
275
294
  and pid_exists(sync_session.process_id)
276
295
  ):
@@ -283,7 +302,7 @@ class NetworkSyncConnection(Connection):
283
302
  try:
284
303
  self._get_sync_session(sync_session)
285
304
  except HTTPError as e:
286
- raise_from(MorangoResumeSyncError("Failure resuming sync session"), e)
305
+ raise MorangoResumeSyncError("Failure resuming sync session") from e
287
306
 
288
307
  # update process id
289
308
  sync_session.process_id = os.getpid()
@@ -337,11 +356,15 @@ class NetworkSyncConnection(Connection):
337
356
  cert_chain_response = self._get_certificate_chain(
338
357
  params={"ancestors_of": parent_cert.id}
339
358
  )
340
-
341
- # upon receiving cert chain from server, we attempt to save the chain into our records
342
- Certificate.save_certificate_chain(
343
- cert_chain_response.json(), expected_last_id=parent_cert.id
344
- )
359
+ cert_chain = cert_chain_response.json()
360
+ with transaction.atomic():
361
+ lock_partitions(DBBackend, sync_filter=Filter(cert_chain[0]["id"]))
362
+ # check again, now that we have a lock
363
+ if not Certificate.objects.filter(id=parent_cert.id).exists():
364
+ # upon receiving cert chain from server, we attempt to save the chain into our records
365
+ Certificate.save_certificate_chain(
366
+ cert_chain, expected_last_id=parent_cert.id
367
+ )
345
368
 
346
369
  csr_key = Key()
347
370
  # build up data for csr
@@ -424,7 +447,7 @@ class NetworkSyncConnection(Connection):
424
447
  # convert user arguments into query str for passing to auth layer
425
448
  if isinstance(userargs, dict):
426
449
  userargs = "&".join(
427
- ["{}={}".format(key, val) for (key, val) in iteritems(userargs)]
450
+ ["{}={}".format(key, val) for (key, val) in userargs.items()]
428
451
  )
429
452
  return self.session.post(
430
453
  self.urlresolve(api_urls.CERTIFICATE), json=data, auth=(userargs, password)
@@ -575,10 +598,8 @@ class TransferClient(object):
575
598
  "sync_connection",
576
599
  "sync_session",
577
600
  "controller",
578
- "current_transfer_session",
579
601
  "signals",
580
- "local_context",
581
- "remote_context",
602
+ "context",
582
603
  )
583
604
 
584
605
  def __init__(self, sync_connection, sync_session, controller):
@@ -590,51 +611,45 @@ class TransferClient(object):
590
611
  self.sync_connection = sync_connection
591
612
  self.sync_session = sync_session
592
613
  self.controller = controller
593
- self.current_transfer_session = None
594
614
  self.signals = SyncClientSignals()
595
615
 
596
- # TODO: come up with strategy to use only one context here
597
- self.remote_context = NetworkSessionContext(
598
- sync_connection, sync_session=sync_session
599
- )
600
- self.local_context = LocalSessionContext(
601
- sync_session=sync_session, capabilities=self.remote_context.capabilities
616
+ capabilities = sync_connection.server_info.get("capabilities", [])
617
+ self.context = CompositeSessionContext(
618
+ [LocalSessionContext(), NetworkSessionContext(sync_connection)],
619
+ sync_session=sync_session,
620
+ capabilities=capabilities,
602
621
  )
622
+ self.controller.context = self.context
603
623
 
604
- def proceed_to_and_wait_for(self, stage):
605
- contexts = (self.local_context, self.remote_context)
606
- for context in contexts:
607
- max_interval = 1 if context is self.local_context else 5
608
- result = self.controller.proceed_to_and_wait_for(
609
- stage, context=context, max_interval=max_interval
610
- )
611
- if result == transfer_statuses.ERRORED:
612
- raise_from(
613
- MorangoError("Stage `{}` failed".format(stage)),
614
- context.error,
615
- )
624
+ @property
625
+ def current_transfer_session(self):
626
+ return self.context.transfer_session
627
+
628
+ def proceed_to_and_wait_for(self, stage, error_msg=None, callback=None):
629
+ """
630
+ Raises an exception if an ERROR result is received from calling `proceed_to_and_wait_for`
631
+ :param stage: The stage to proceed to
632
+ :param error_msg: An error message str to use as the exception message if it errors
633
+ :param callback: A callback to pass along to the controller
634
+ """
635
+ result = self.controller.proceed_to_and_wait_for(stage, callback=callback)
636
+ if result == transfer_statuses.ERRORED:
637
+ raise MorangoError(
638
+ error_msg or "Stage `{}` failed".format(self.context.stage)
639
+ ) from self.context.error
616
640
 
617
641
  def initialize(self, sync_filter):
618
642
  """
619
643
  :param sync_filter: Filter
620
644
  """
621
645
  # set filter on controller
622
- self.local_context.update(sync_filter=sync_filter)
623
- self.remote_context.update(sync_filter=sync_filter)
646
+ self.context.update(sync_filter=sync_filter)
624
647
 
625
- # initialize the transfer session locally
626
- status = self.controller.proceed_to_and_wait_for(
627
- transfer_stages.INITIALIZING, context=self.local_context, max_interval=1
648
+ # initialize the transfer session
649
+ self.proceed_to_and_wait_for(
650
+ transfer_stages.INITIALIZING,
651
+ error_msg="Failed to initialize transfer session",
628
652
  )
629
- if status == transfer_statuses.ERRORED:
630
- raise_from(
631
- MorangoError("Failed to initialize transfer session"),
632
- self.local_context.error,
633
- )
634
-
635
- # copy the transfer session to local state and update remote controller context
636
- self.current_transfer_session = self.local_context.transfer_session
637
- self.remote_context.update(transfer_session=self.current_transfer_session)
638
653
 
639
654
  self.signals.session.started.fire(
640
655
  transfer_session=self.current_transfer_session
@@ -643,21 +658,21 @@ class TransferClient(object):
643
658
  # backwards compatibility for the queuing signal as it included both serialization
644
659
  # and queuing originally
645
660
  with self.signals.queuing.send(transfer_session=self.current_transfer_session):
646
- # proceeding to serialization on remote will trigger initialization as well
647
- self.proceed_to_and_wait_for(transfer_stages.SERIALIZING)
661
+ # proceeding to queuing on remote will trigger initialization and serialization as well
648
662
  self.proceed_to_and_wait_for(transfer_stages.QUEUING)
649
663
 
650
664
  def run(self):
665
+ """
666
+ Execute the transferring portion of the sync
667
+ """
651
668
  with self.signals.transferring.send(
652
669
  transfer_session=self.current_transfer_session
653
670
  ) as status:
654
- self._transfer(callback=status.in_progress.fire)
671
+ self.proceed_to_and_wait_for(
672
+ transfer_stages.TRANSFERRING, callback=status.in_progress.fire
673
+ )
655
674
 
656
675
  def finalize(self):
657
- # if not initialized, we don't need to finalize
658
- if not self.current_transfer_session:
659
- return
660
-
661
676
  with self.signals.dequeuing.send(
662
677
  transfer_session=self.current_transfer_session
663
678
  ):
@@ -667,26 +682,6 @@ class TransferClient(object):
667
682
  self.signals.session.completed.fire(
668
683
  transfer_session=self.current_transfer_session
669
684
  )
670
- self.current_transfer_session = None
671
-
672
- def _transfer(self, callback=None):
673
- result = transfer_statuses.PENDING
674
-
675
- while result not in transfer_statuses.FINISHED_STATES:
676
- result = self.controller.proceed_to(
677
- transfer_stages.TRANSFERRING, context=self.remote_context
678
- )
679
- self.local_context.update(
680
- stage=transfer_stages.TRANSFERRING, stage_status=result
681
- )
682
- if callback is not None:
683
- callback()
684
-
685
- if result == transfer_statuses.ERRORED:
686
- raise_from(
687
- MorangoError("Failure occurred during transfer"),
688
- self.remote_context.error,
689
- )
690
685
 
691
686
 
692
687
  class PushClient(TransferClient):
@@ -696,8 +691,7 @@ class PushClient(TransferClient):
696
691
 
697
692
  def __init__(self, *args, **kwargs):
698
693
  super(PushClient, self).__init__(*args, **kwargs)
699
- self.local_context.update(is_push=True)
700
- self.remote_context.update(is_push=True)
694
+ self.context.update(is_push=True)
701
695
 
702
696
 
703
697
  class PullClient(TransferClient):
@@ -707,5 +701,4 @@ class PullClient(TransferClient):
707
701
 
708
702
  def __init__(self, *args, **kwargs):
709
703
  super(PullClient, self).__init__(*args, **kwargs)
710
- self.local_context.update(is_push=False)
711
- self.remote_context.update(is_push=False)
704
+ self.context.update(is_push=False)
morango/sync/utils.py CHANGED
@@ -230,3 +230,21 @@ class SyncSignalGroup(SyncSignal):
230
230
  Fires the `completed` signal.
231
231
  """
232
232
  self.completed.fire()
233
+
234
+
235
+ def lock_partitions(backend, sync_filter=None, shared=False):
236
+ """
237
+ Lock all partitions for a filter, and really lock everything if the filter is null
238
+
239
+ :type backend: morango.sync.backends.base.BaseSQLWrapper
240
+ :type sync_filter: morango.models.certificates.Filter|None
241
+ :type shared: bool
242
+ """
243
+ if sync_filter is None:
244
+ backend._lock_all_partitions(shared=shared)
245
+ else:
246
+ # parse the first 32 chars to obtain the primary partitions in the filter
247
+ partitions = set(f[:32] for f in sync_filter)
248
+ # for every unique partition, acquire a lock
249
+ for partition in partitions:
250
+ backend._lock_partition(partition, shared=shared)
morango/urls.py CHANGED
@@ -1,4 +1,4 @@
1
- from django.conf.urls import include
2
- from django.conf.urls import url
1
+ from django.urls import include
2
+ from django.urls import path
3
3
 
4
- urlpatterns = [url(r"^api/morango/v1/", include("morango.api.urls"))]
4
+ urlpatterns = [path("api/morango/v1/", include("morango.api.urls"))]
morango/utils.py CHANGED
@@ -1,14 +1,13 @@
1
1
  import os
2
- import six
3
2
  from importlib import import_module
4
3
 
5
4
  from django.conf import settings
6
5
 
7
6
  from morango.constants import settings as default_settings
8
7
  from morango.constants.capabilities import ALLOW_CERTIFICATE_PUSHING
9
- from morango.constants.capabilities import GZIP_BUFFER_POST
10
8
  from morango.constants.capabilities import ASYNC_OPERATIONS
11
9
  from morango.constants.capabilities import FSIC_V2_FORMAT
10
+ from morango.constants.capabilities import GZIP_BUFFER_POST
12
11
 
13
12
 
14
13
  def do_import(import_string):
@@ -32,7 +31,7 @@ class Settings(object):
32
31
  def __getattribute__(self, key):
33
32
  """Coalesces settings with the defaults"""
34
33
  value = getattr(settings, key, getattr(default_settings, key, None))
35
- if key == "MORANGO_INSTANCE_INFO" and isinstance(value, six.string_types):
34
+ if key == "MORANGO_INSTANCE_INFO" and isinstance(value, str):
36
35
  value = dict(do_import(value))
37
36
  return value
38
37
 
@@ -1,30 +1,47 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: morango
3
- Version: 0.6.11
3
+ Version: 0.8.6
4
4
  Summary: Pure Python sqlite-based Django DB replication engine.
5
5
  Home-page: https://github.com/learningequality/morango
6
6
  Author: Learning Equality
7
7
  Author-email: dev@learningequality.org
8
8
  License: MIT
9
9
  Keywords: database,syncing,morango
10
- Platform: UNKNOWN
11
10
  Classifier: Intended Audience :: Developers
12
11
  Classifier: License :: OSI Approved :: MIT License
13
12
  Classifier: Natural Language :: English
14
13
  Classifier: Development Status :: 4 - Beta
15
- Classifier: Programming Language :: Python :: 2.7
16
- Classifier: Programming Language :: Python :: 3.4
17
- Classifier: Programming Language :: Python :: 3.5
18
14
  Classifier: Programming Language :: Python :: 3.6
15
+ Classifier: Programming Language :: Python :: 3.7
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Requires-Python: >=3.6, <3.14
19
23
  Description-Content-Type: text/markdown
20
- Requires-Dist: django (<2,>=1.10)
21
- Requires-Dist: django-mptt (<0.10.0)
22
- Requires-Dist: rsa (<3.5,>=3.4.2)
23
- Requires-Dist: djangorestframework (==3.9.1)
24
- Requires-Dist: django-ipware (<1.2,>=1.1.6)
25
- Requires-Dist: future (==0.16.0)
24
+ License-File: LICENSE
25
+ License-File: AUTHORS.md
26
+ Requires-Dist: django<4,>=3
27
+ Requires-Dist: django-mptt>0.10.0
28
+ Requires-Dist: rsa<4.10
29
+ Requires-Dist: djangorestframework>3.10
30
+ Requires-Dist: django-ipware==4.0.2
26
31
  Requires-Dist: requests
27
32
  Requires-Dist: ifcfg
33
+ Dynamic: author
34
+ Dynamic: author-email
35
+ Dynamic: classifier
36
+ Dynamic: description
37
+ Dynamic: description-content-type
38
+ Dynamic: home-page
39
+ Dynamic: keywords
40
+ Dynamic: license
41
+ Dynamic: license-file
42
+ Dynamic: requires-dist
43
+ Dynamic: requires-python
44
+ Dynamic: summary
28
45
 
29
46
  # Morango
30
47
 
@@ -57,5 +74,3 @@ make docs
57
74
  # auto-build and refresh docs on edit
58
75
  make docs-autobuild
59
76
  ```
60
-
61
-
@@ -0,0 +1,79 @@
1
+ morango/__init__.py,sha256=VpASnrti7EGWxUfSWGgERUfe7NLJltfVXYosOzHbpPg,22
2
+ morango/apps.py,sha256=bs_LaTt1038Oh0OOWqaZKpF8rL9cg8rqW4hftIILPw8,559
3
+ morango/errors.py,sha256=okjt7FqSHIf3x9SmnmoW6aqNJ_g7Ye3Fx7tuQSNUYEI,1223
4
+ morango/proquint.py,sha256=YRjgceaYD_wDE8R-21AK5m6MSzbarIBP_kDxJ_gHsbQ,3679
5
+ morango/registry.py,sha256=LcHyrNqcqSBAEHJDMS0pinKKzkxRTZjxEyAeZhtrKgo,9480
6
+ morango/urls.py,sha256=pIjnq01BUNxnC0BucRXMr_0H366U5l88k65r67JtHK8,131
7
+ morango/utils.py,sha256=psppzkIjlrmWI2bq2a1wGW0f_0VhdszYleLbm4C4aAs,3652
8
+ morango/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ morango/api/fields.py,sha256=LjEuUrF86JABN45HSXHpUoWHHBDeb73jqD_uS6QY0EA,273
10
+ morango/api/parsers.py,sha256=PxNHz94V3VrSCEVYxuTRO2h8mTg9H7WSy891BxUvsac,557
11
+ morango/api/permissions.py,sha256=B3biuWZfm1VXbIO5pKR2jYBNQBfgY2oKfX3dargtb6A,3550
12
+ morango/api/serializers.py,sha256=GtG_6wcJst68o3NHuIi2q_pX9cOIIIrqmHDZRgR-BdE,5444
13
+ morango/api/urls.py,sha256=D3VFRf4wm1u-Pswu6ZGqJoG-UHQFTI7GGXcqNyz0t5Y,1037
14
+ morango/api/viewsets.py,sha256=qxc4RhQrnJKVAq6F1X9Yo1tv3eGmvF0QJ8-gBrR9lOA,20859
15
+ morango/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ morango/constants/api_urls.py,sha256=mmGJyyVtrMxjIMAF2iBHbjGVndsvW_z1IQH-GhW3L-g,453
17
+ morango/constants/capabilities.py,sha256=KaTQpfpg6l4G0aGXJnOKi7ibbshdg-1fA9aLl-Qc_Oc,166
18
+ morango/constants/settings.py,sha256=dygRwRZhYkTdRhEltc5QqdoQBLkuZ-f9fdFMVrCaDpQ,2009
19
+ morango/constants/transfer_stages.py,sha256=mp-1V22ba0gKdylJgnUJ8Jh-Qs9-3-LsY_Q_cxRUgmI,1378
20
+ morango/constants/transfer_statuses.py,sha256=BU3_dx7RxXpWF3pDjC9JsDe2xzKSGiqZFVBbdIY7_aQ,509
21
+ morango/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ morango/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ morango/management/commands/cleanupsyncs.py,sha256=Huaz8etLNbmW8X-Vy4x1QY801JYln0iyaeUTJF2L3kE,4833
24
+ morango/migrations/0001_initial.py,sha256=ZFEKusKCIUsgUuHpF-eWqEqu7FrhO2ikkfu5KfYfXx0,11892
25
+ morango/migrations/0001_squashed_0024_auto_20240129_1757.py,sha256=K-71iKz9cKpb-zrxuoDJVWafZM3U_hM6EpX6aNzsFjM,21770
26
+ morango/migrations/0002_auto_20170511_0400.py,sha256=9EQcTl2xHd-rwx_NWCLzo8GU3u7MFA3WOryyKYEbHUw,2263
27
+ morango/migrations/0002_store_idx_morango_deserialize.py,sha256=AbfcuoovuViGRugR5lH86E3JgfgBa_4koTp5vMiUAh8,568
28
+ morango/migrations/0003_auto_20170519_0543.py,sha256=5YfHW6IcT57tgaXW6pnWHmL6kfgauTW2nao9zQr3nP8,641
29
+ morango/migrations/0004_auto_20170520_2112.py,sha256=T7rGjSPXUGnanDr5SJKj5SHZEQYSIaND60vRaK3DObw,517
30
+ morango/migrations/0005_auto_20170629_2139.py,sha256=PL1I3wybrDgDxY8hSmZmUfpxOulr6O2ywui7ue_dvKw,654
31
+ morango/migrations/0006_instanceidmodel_system_id.py,sha256=L4Y5VN1-IBnBPs-lyQrJronf5lrXjvwGFSTKuug-jjo,435
32
+ morango/migrations/0007_auto_20171018_1615.py,sha256=otylWm8eBH8W_fJx-W5C6e8mfhqPdrb-6SZh0GCpFZo,8149
33
+ morango/migrations/0008_auto_20171114_2217.py,sha256=x_2rKsqfH-yRHmuV3qnBgPRBBe0C2Z08aryWcBZU_jw,2085
34
+ morango/migrations/0009_auto_20171205_0252.py,sha256=XxkPK4AsERDw6ZBojS0M1ElMTuhVjxRWDSMDMnvPbDM,359
35
+ morango/migrations/0010_auto_20171206_1615.py,sha256=2Svr2sMBO31fOzm26HdgAL-YmHGOzgWnDMEuoaDWrOU,800
36
+ morango/migrations/0011_sharedkey.py,sha256=fZkD4-2d628Cg0-ekvQnI_CAIVCuei4R2zre9_i87ms,926
37
+ morango/migrations/0012_auto_20180927_1658.py,sha256=29ibsHWltRyUk3lXR3ZLzmeCY2SRWpW7KXU8zmTzIfk,946
38
+ morango/migrations/0013_auto_20190627_1513.py,sha256=eG6pdMh_xqz9nfn9z0F987rR12i6JswSFFKCjsHwsCQ,464
39
+ morango/migrations/0014_syncsession_extra_fields.py,sha256=11qg91F0ZCaxh3AS17xEUH5xlxBDyWSi3RRgbE593xE,440
40
+ morango/migrations/0015_auto_20200508_2104.py,sha256=nD3FkddbgWYDoIau79X5-gq_xMjlJV_EQF14MYff-Fc,667
41
+ morango/migrations/0016_store_deserialization_error.py,sha256=yGenUzUrcIzWDR2_7h0tU_8M-2tBC2-ldjcPrV6510M,441
42
+ morango/migrations/0017_store_last_transfer_session_id.py,sha256=oVQUlvo247JgecQ2NY-Xb5kyoK4rRhb09YbrRj1enmo,521
43
+ morango/migrations/0018_auto_20210714_2216.py,sha256=ghAZmcFXqlWPuSiFt-ikwfZJg-3IKOXiC_sa6nbzme0,1599
44
+ morango/migrations/0019_auto_20220113_1807.py,sha256=jRbNz8OQScW4KN-JsY_KZb7yy5kSO30iWhSAhCHC6w4,1521
45
+ morango/migrations/0020_postgres_fix_nullable.py,sha256=QHgSdEzmIzpLn0HXDWRepmBhvc7S-1emErNQVyrYAfA,1206
46
+ morango/migrations/0021_store_partition_index_create.py,sha256=B9p20AQA0BVqlfmaPYd4RAghYtJKvrdzafWIUAbV7w0,2209
47
+ morango/migrations/0022_rename_instance_fields.py,sha256=xn2PGlgkHOPRYBHrbGdh6XGGHl_X5HPFEIgmGIG0Ycw,592
48
+ morango/migrations/0023_add_instance_id_fields.py,sha256=ovO_AVGLPlLpEc72UhrJ0TpFYCci_CzDcEdmnNJzLew,635
49
+ morango/migrations/0024_auto_20240129_1757.py,sha256=Buy1T4PiTFo8zP7p2HY-3gmiJurQsSnu7CBgTSvOxXg,762
50
+ morango/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
+ morango/models/__init__.py,sha256=ws6VwRPs7iKLRfFSuRpUESTZlGEmSk0Nv61chh-Gn2Q,1722
52
+ morango/models/certificates.py,sha256=4s7YTNhmTXLyPuhpZZay-Dei8pdq2WqlOshNwGxzlOE,19380
53
+ morango/models/core.py,sha256=-yZ01bS0nXNV0BL_wTHEc4x-7Hh_69hn3MyUunGK2SU,39080
54
+ morango/models/fsic_utils.py,sha256=B9RM-uPZFvEXWbsPeeit5qA21gKGpA0NkKCLFIdNI_0,7130
55
+ morango/models/manager.py,sha256=fzzX7k8qV5W5dMBb0ASOBNRJRpvQZbEG1hgyuMtzt4g,163
56
+ morango/models/query.py,sha256=eHcf9PYYEDShN6lRng7Ow9RJwNFPdKI1hDbVUXeyl9U,869
57
+ morango/models/signals.py,sha256=R17Fqddbg2AgAVnH4oZPrDQMWi94R2XsLmnUbrgKS2I,478
58
+ morango/models/utils.py,sha256=8J138TR3FkslovfUWkLLEpODieFtmHKne7Lm1I8iPXk,6588
59
+ morango/models/fields/__init__.py,sha256=dzxg8-_v3irXkPnyCsiVDur0gG0RJfKzFcshjL6Y-9s,225
60
+ morango/models/fields/crypto.py,sha256=Q_y7s-UpFEz9zXFaAkEDOmqwx4b19F7wbTafpd_XPp4,14509
61
+ morango/models/fields/uuids.py,sha256=w7M8KlA8oLPascq14tcor89V92IGxx2XtdweS-0lF6A,4275
62
+ morango/sync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
+ morango/sync/context.py,sha256=r3sHkmb7OYCqLm3C0ADv2qvdXeZHTBwLGe6wkMdOMGc,18163
64
+ morango/sync/controller.py,sha256=ZfvXFNptduxhtF72E07o9Dh3Fcnk7xSyjvMTZ---oZw,11666
65
+ morango/sync/operations.py,sha256=ErZ-W4MiCUVl_vn0pFi8DWGgT1C9wOAH4AagCMdLBOs,69502
66
+ morango/sync/session.py,sha256=9CTLhpEPM8q1M_b2HHxouZbzm7vBJoImoqPWqdodgII,3853
67
+ morango/sync/syncsession.py,sha256=fhm7r3DHeBXDSWG0k7I2slhgxe3ebtmXouZHTzUQ9cs,25846
68
+ morango/sync/utils.py,sha256=BrG8CYVmaAhRNa_HYuPCYSnncskSsW_ig0Cf7xsY2mo,8792
69
+ morango/sync/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
+ morango/sync/backends/base.py,sha256=zQP5hOB2Qw0qv0G00LXFHl2GQ3b0bheyujdNVl5rT-8,10867
71
+ morango/sync/backends/postgres.py,sha256=N0IeXN67AkdQKlD20b8LQZ1ocdarapL8pqu8FQAc6lM,19850
72
+ morango/sync/backends/sqlite.py,sha256=vg7kWFIF346-NjVwexYfjYbrrOyzzvcCmk5uUkqhabw,12850
73
+ morango/sync/backends/utils.py,sha256=UvyYByzRenOwtDvLPO_1wIOE8Yr88FnuAM2c4VSk7uQ,5145
74
+ morango-0.8.6.dist-info/licenses/AUTHORS.md,sha256=9Ussd3Fq3RPjHyQT_3AyGss5jiVbn858WQuIBOi1ajI,276
75
+ morango-0.8.6.dist-info/licenses/LICENSE,sha256=RkI6MjmrrTKa3aoPAVyk9QdeLZclcrKds6FH3phRSRU,1074
76
+ morango-0.8.6.dist-info/METADATA,sha256=Qy4IvvYvDLsslhq2TzulHvZDzqEBvPBhMjFCwpBi0IQ,2819
77
+ morango-0.8.6.dist-info/WHEEL,sha256=AeO2BvogYWm3eGaHCvhzmUYt8ia7KfURiHzO_1atlys,109
78
+ morango-0.8.6.dist-info/top_level.txt,sha256=pzREWN0EeEq3yHDRast9XI081Ow_rtcm7WV0ZQTlIPY,8
79
+ morango-0.8.6.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.37.1)
2
+ Generator: setuptools (79.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any
@@ -1,33 +0,0 @@
1
- from mptt import managers
2
- from mptt import models
3
- from mptt import querysets
4
-
5
- from .manager import SyncableModelManager
6
- from .query import SyncableModelQuerySet
7
-
8
-
9
- class MorangoTreeQuerySet(querysets.TreeQuerySet, SyncableModelQuerySet):
10
- pass
11
-
12
-
13
- class MorangoMPTTTreeManager(managers.TreeManager, SyncableModelManager):
14
- def get_queryset(self):
15
- return MorangoTreeQuerySet(self.model, using=self._db)
16
-
17
- def _mptt_update(self, qs=None, **items):
18
- items["update_dirty_bit_to"] = None
19
- return super(MorangoMPTTTreeManager, self)._mptt_update(qs, **items)
20
-
21
-
22
- class MorangoMPTTModel(models.MPTTModel):
23
- """
24
- Any model that inherits from ``SyncableModel`` that also wants to inherit from ``MPTTModel`` should instead inherit
25
- from ``MorangoMPTTModel``, which modifies some behavior to make it safe for the syncing system.
26
- """
27
-
28
- _internal_mptt_fields_not_to_serialize = ("lft", "rght", "tree_id", "level")
29
-
30
- objects = MorangoMPTTTreeManager()
31
-
32
- class Meta:
33
- abstract = True