From cc35443bfaa14bdd61c4b2ba8f13943326b2da2c Mon Sep 17 00:00:00 2001
From: Erwan Rouchet <rouchet@teklia.com>
Date: Thu, 25 Nov 2021 10:44:59 +0000
Subject: [PATCH] Bump to Python 3.8

---
 .gitlab-ci.yml                                |  4 ++--
 .pre-commit-config.yaml                       |  3 ---
 Dockerfile                                    |  4 ++--
 Dockerfile.binary                             |  4 ++--
 arkindex/project/checks.py                    | 15 +++++++++++++-
 arkindex/project/tests/test_checks.py         | 20 ++++++++++++++++++-
 arkindex/project/tools.py                     | 13 +++++++-----
 .../element_move_with_children.sql            |  8 ++++----
 base/Dockerfile                               |  2 +-
 9 files changed, 52 insertions(+), 21 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 97410fcd4f..0493528c74 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -11,7 +11,7 @@ include:
 
 # For jobs that run backend scripts directly
 .backend-setup:
-  image: registry.gitlab.com/arkindex/backend/base:django-3.2.6
+  image: registry.gitlab.com/arkindex/backend/base:python-3.8
 
   cache:
     paths:
@@ -65,7 +65,7 @@ backend-tests:
     - codecov
 
 backend-lint:
-  image: python:3.7
+  image: python:3.8
   stage: test
 
   except:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 091bc17058..6a49744b1b 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -34,6 +34,3 @@ repos:
   - repo: meta
     hooks:
       - id: check-useless-excludes
-
-default_language_version:
-  python: python3.7
diff --git a/Dockerfile b/Dockerfile
index 95b0f2fcf5..a91f387fc4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,10 +1,10 @@
-FROM registry.gitlab.com/arkindex/backend/base:django-3.2.6 as build
+FROM registry.gitlab.com/arkindex/backend/base:python-3.8 as build
 
 RUN mkdir build
 ADD . build
 RUN cd build && python3 setup.py sdist
 
-FROM registry.gitlab.com/arkindex/backend/base:django-3.2.6
+FROM registry.gitlab.com/arkindex/backend/base:python-3.8
 ARG PONOS_BRANCH=master
 ARG PONOS_ID=10017043
 ARG TRANSKRIBUS_BRANCH=master
diff --git a/Dockerfile.binary b/Dockerfile.binary
index f879099b00..56c1b2156f 100644
--- a/Dockerfile.binary
+++ b/Dockerfile.binary
@@ -1,4 +1,4 @@
-FROM python:3.7-slim AS compilation
+FROM python:3.8-slim AS compilation
 
 RUN apt-get update && apt-get install --no-install-recommends -y build-essential wget
 
@@ -61,7 +61,7 @@ RUN python -m nuitka \
       arkindex/manage.py
 
 # Start over from a clean setup
-FROM registry.gitlab.com/arkindex/backend/base:django-3.2.6 as build
+FROM registry.gitlab.com/arkindex/backend/base:python-3.8 as build
 
 # Import files from compilation
 RUN mkdir /usr/share/arkindex
diff --git a/arkindex/project/checks.py b/arkindex/project/checks.py
index 49f8e43cac..8cb441b7d5 100644
--- a/arkindex/project/checks.py
+++ b/arkindex/project/checks.py
@@ -1,7 +1,8 @@
 import os.path
+import sys
 
 import yaml
-from django.core.checks import Error, Warning, register
+from django.core.checks import Critical, Error, Warning, register
 
 from ponos.recipe import parse_recipe
 
@@ -163,3 +164,15 @@ def public_hostname_check(*args, **kwargs):
             id='arkindex.W008',
         )]
     return []
+
+
+@register()
+def python_version_check(*args, **kwargs):
+    if sys.version_info.minor < 8:
+        return [
+            Critical(
+                'Arkindex requires Python 3.8 or later.',
+                id='arkindex.C001',
+            )
+        ]
+    return []
diff --git a/arkindex/project/tests/test_checks.py b/arkindex/project/tests/test_checks.py
index eacd5768ca..a28e8e3730 100644
--- a/arkindex/project/tests/test_checks.py
+++ b/arkindex/project/tests/test_checks.py
@@ -2,7 +2,7 @@ from pathlib import Path
 from unittest.mock import patch
 
 from django.conf import settings
-from django.core.checks import Error, Warning
+from django.core.checks import Critical, Error, Warning
 from django.test import TestCase, override_settings
 from django.urls import path
 
@@ -180,3 +180,21 @@ class ChecksTestCase(TestCase):
 
         settings.PUBLIC_HOSTNAME = 'https://darkindex.lol'
         self.assertListEqual(public_hostname_check(), [])
+
+    @patch('arkindex.project.checks.sys.version_info')
+    def test_python_version_check(self, version_info_mock):
+        from arkindex.project.checks import python_version_check
+
+        version_info_mock.minor = 7
+        self.assertListEqual(python_version_check(), [
+            Critical(
+                'Arkindex requires Python 3.8 or later.',
+                id='arkindex.C001',
+            )
+        ])
+
+        version_info_mock.minor = 8
+        self.assertListEqual(python_version_check(), [])
+
+        version_info_mock.minor = 9
+        self.assertListEqual(python_version_check(), [])
diff --git a/arkindex/project/tools.py b/arkindex/project/tools.py
index 319da45bd6..6a7b9ae870 100644
--- a/arkindex/project/tools.py
+++ b/arkindex/project/tools.py
@@ -76,17 +76,20 @@ class BulkMap(Sized, Iterable):
         assert isinstance(iterable, Sized), 'iterable must implement __len__'
         self.func = func
         self.iterable = iterable
+        self._cached_length = None
 
     def __getitem__(self, key):
         assert isinstance(key, slice), 'BulkMap only supports slicing'
         return BulkMap(self.func, self.iterable[key])
 
     def __len__(self):
-        # Proxy __len__ without calling the function
-        if hasattr(self.iterable, 'count'):
-            return self.iterable.count()
-        else:
-            return len(self.iterable)
+        if self._cached_length is None:
+            # Proxy __len__ without calling the function
+            if hasattr(self.iterable, 'count'):
+                self._cached_length = self.iterable.count()
+            else:
+                self._cached_length = len(self.iterable)
+        return self._cached_length
 
     def __iter__(self):
         return iter(self.func(list(self.iterable)))
diff --git a/arkindex/sql_validation/element_move_with_children.sql b/arkindex/sql_validation/element_move_with_children.sql
index 3bc38631ae..df3adf3f17 100644
--- a/arkindex/sql_validation/element_move_with_children.sql
+++ b/arkindex/sql_validation/element_move_with_children.sql
@@ -90,10 +90,10 @@ INSERT INTO "documents_elementpath" ("id",
                                      "element_id",
                                      "path",
                                      "ordering")
-VALUES ('{paths_ids[4]}'::uuid, '{children_ids[3]}'::uuid, ARRAY['{destination_id}'::uuid,'{source_id}'::uuid]::uuid[], 0),
+VALUES ('{paths_ids[4]}'::uuid, '{children_ids[0]}'::uuid, ARRAY['{destination_id}'::uuid,'{source_id}'::uuid]::uuid[], 2),
        ('{paths_ids[5]}'::uuid, '{children_ids[2]}'::uuid, ARRAY['{destination_id}'::uuid,'{source_id}'::uuid]::uuid[], 1),
-       ('{paths_ids[6]}'::uuid, '{children_ids[1]}'::uuid, ARRAY['{destination_id}'::uuid,'{source_id}'::uuid]::uuid[], 0),
-       ('{paths_ids[7]}'::uuid, '{children_ids[0]}'::uuid, ARRAY['{destination_id}'::uuid,'{source_id}'::uuid]::uuid[], 2),
-       ('{paths_ids[8]}'::uuid, '{source_id}'::uuid, ARRAY['{destination_id}'::uuid]::uuid[], 3);
+       ('{paths_ids[6]}'::uuid, '{children_ids[3]}'::uuid, ARRAY['{destination_id}'::uuid,'{source_id}'::uuid]::uuid[], 0),
+       ('{paths_ids[7]}'::uuid, '{source_id}'::uuid, ARRAY['{destination_id}'::uuid]::uuid[], 3),
+       ('{paths_ids[8]}'::uuid, '{children_ids[1]}'::uuid, ARRAY['{destination_id}'::uuid,'{source_id}'::uuid]::uuid[], 0);
 
 RELEASE SAVEPOINT "{savepoints[1]}"
diff --git a/base/Dockerfile b/base/Dockerfile
index 2e3bb8bdde..85ff115433 100644
--- a/base/Dockerfile
+++ b/base/Dockerfile
@@ -1,4 +1,4 @@
-FROM python:3.7-slim
+FROM python:3.8-slim
 
 # Install some runtime deps and python dependencies that are slow to install or require specific build deps
 ADD requirements.txt bootstrap.sh /
-- 
GitLab