From 66eac09760ec495679c44c2bbdeeba7ee8118861 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Fri, 20 Aug 2021 16:56:41 +0200
Subject: [PATCH 08/31] misc: fix import-error pylint error (#56288)

---
 combo/apps/lingo/models.py       |  4 +-
 combo/apps/wcs/views.py          |  6 +--
 combo/data/models.py             |  4 +-
 combo/public/views.py            | 11 ++--
 combo/settings.py                |  2 +-
 combo/urls.py                    |  2 +-
 combo/utils/misc.py              |  6 +--
 combo/utils/requests_wrapper.py  | 12 ++---
 combo/utils/signature.py         |  8 +--
 combo/utils/spooler.py           |  4 +-
 combo/wsgi.py                    |  2 +-
 tests/test_calendar.py           |  6 +--
 tests/test_dashboard.py          |  4 +-
 tests/test_lingo_payment.py      | 90 ++++++++++++++++----------------
 tests/test_lingo_remote_regie.py | 42 +++++++--------
 tests/test_manager.py            |  4 +-
 tests/test_public.py             | 50 +++++++++---------
 tests/test_requests.py           | 30 +++++------
 tests/test_wcs.py                |  4 +-
 19 files changed, 145 insertions(+), 146 deletions(-)

diff --git a/combo/apps/lingo/models.py b/combo/apps/lingo/models.py
index 40876d73..a1125693 100644
--- a/combo/apps/lingo/models.py
+++ b/combo/apps/lingo/models.py
@@ -21,6 +21,7 @@ import logging
 import re
 from decimal import Decimal
 from functools import reduce
+from urllib import parse
 
 import eopayment
 from dateutil import parser
@@ -39,7 +40,6 @@ from django.urls import reverse
 from django.utils import dateparse, timezone
 from django.utils.encoding import force_bytes, python_2_unicode_compatible
 from django.utils.formats import localize
-from django.utils.six.moves.urllib import parse as urlparse
 from django.utils.timezone import make_aware, now, utc
 from django.utils.translation import ugettext_lazy as _
 from requests import RequestException
@@ -557,7 +557,7 @@ class Regie(models.Model):
         payment_url = reverse('view-item', kwargs={'regie_id': self.id, 'item_crypto_id': invoice.crypto_id})
         ctx = settings.TEMPLATE_VARS.copy()
         ctx['invoice'] = invoice
-        ctx['payment_url'] = urlparse.urljoin(settings.SITE_BASE_URL, payment_url)
+        ctx['payment_url'] = parse.urljoin(settings.SITE_BASE_URL, payment_url)
         ctx['portal_url'] = settings.SITE_BASE_URL
         subject = render_to_string([subject_template], ctx).strip()
         text_body = render_to_string([text_body_template], ctx)
diff --git a/combo/apps/wcs/views.py b/combo/apps/wcs/views.py
index cbf5cb4f..87f51bb7 100644
--- a/combo/apps/wcs/views.py
+++ b/combo/apps/wcs/views.py
@@ -15,6 +15,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import re
+from urllib import parse
 
 import ratelimit.utils
 from django.conf import settings
@@ -22,7 +23,6 @@ from django.contrib import messages
 from django.core.exceptions import DisallowedRedirect, PermissionDenied
 from django.http import HttpResponseBadRequest, HttpResponseRedirect, JsonResponse
 from django.utils.http import urlquote
-from django.utils.six.moves.urllib import parse as urlparse
 from django.utils.translation import ugettext_lazy as _
 from django.views.decorators.csrf import csrf_exempt
 from django.views.generic import View
@@ -79,9 +79,9 @@ class TrackingCodeView(View):
         code = request.POST['code']
 
         next_url = request.POST.get('url') or '/'
-        next_netloc = urlparse.urlparse(next_url).netloc
+        next_netloc = parse.urlparse(next_url).netloc
         redirect_to_other_domain = bool(
-            next_netloc and next_netloc != urlparse.urlparse(request.build_absolute_uri()).netloc
+            next_netloc and next_netloc != parse.urlparse(request.build_absolute_uri()).netloc
         )
 
         if redirect_to_other_domain and not is_url_from_known_service(next_url):
diff --git a/combo/data/models.py b/combo/data/models.py
index d67b03a5..00d16171 100644
--- a/combo/data/models.py
+++ b/combo/data/models.py
@@ -24,6 +24,7 @@ import logging
 import os
 import re
 import subprocess
+from urllib import parse
 
 import feedparser
 import requests
@@ -51,7 +52,6 @@ from django.utils import timezone
 from django.utils.encoding import force_text, python_2_unicode_compatible, smart_bytes
 from django.utils.html import strip_tags
 from django.utils.safestring import mark_safe
-from django.utils.six.moves.urllib import parse as urlparse
 from django.utils.text import slugify
 from django.utils.timezone import now
 from django.utils.translation import ugettext_lazy as _
@@ -1432,7 +1432,7 @@ class LinkCell(CellBase):
         else:
             extra_context['title'] = self.title or self.url
         url = self.get_url(context)
-        if force_absolute_url and not urlparse.urlparse(url).netloc:
+        if force_absolute_url and not parse.urlparse(url).netloc:
             # create full URL when used in a skeleton
             url = request.build_absolute_uri(url)
         extra_context['url'] = url
diff --git a/combo/public/views.py b/combo/public/views.py
index 193ee672..432e821e 100644
--- a/combo/public/views.py
+++ b/combo/public/views.py
@@ -17,6 +17,7 @@
 import json
 import re
 from itertools import chain
+from urllib import parse
 
 from django.conf import settings
 from django.contrib import messages
@@ -40,8 +41,6 @@ from django.template import engines
 from django.template.loader import TemplateDoesNotExist, get_template
 from django.utils import lorem_ipsum, timezone
 from django.utils.encoding import force_text
-from django.utils.six.moves.urllib import parse as urllib
-from django.utils.six.moves.urllib import parse as urlparse
 from django.utils.translation import ugettext as _
 from django.views.decorators.csrf import csrf_exempt
 from django.views.defaults import page_not_found
@@ -73,7 +72,7 @@ class LoginView(auth_views.LoginView):
             if 'next' not in request.GET:
                 return HttpResponseRedirect(resolve_url('mellon_login'))
             try:
-                quoted_next_url = urllib.quote(request.GET.get('next'))
+                quoted_next_url = parse.quote(request.GET.get('next'))
             except KeyError:
                 return HttpResponseBadRequest('invalid value for "next" parameter')
             return HttpResponseRedirect(resolve_url('mellon_login') + '?next=' + quoted_next_url)
@@ -274,7 +273,7 @@ def skeleton(request):
         response.status_code = 200
         return response
 
-    parsed_source = urlparse.urlparse(source)
+    parsed_source = parse.urlparse(source)
     netloc = parsed_source.netloc
     if parsed_source.scheme == 'https' and netloc.endswith(':443'):
         # somme HTTP client (like Mozilla/1.1 (compatible; MSPIE 2.0; Windows
@@ -300,7 +299,7 @@ def skeleton(request):
             if selected_page is None or len(redirect_url) >= len(selected_page.get_redirect_url()):
                 selected_page = page
 
-        if urlparse.urlparse(redirect_url).netloc == netloc:
+        if parse.urlparse(redirect_url).netloc == netloc:
             same_domain_pages.append(page)
 
     if selected_page is None and same_domain_pages:
@@ -313,7 +312,7 @@ def skeleton(request):
         for service_id in settings.KNOWN_SERVICES or {}:
             for service_key in settings.KNOWN_SERVICES[service_id]:
                 service = settings.KNOWN_SERVICES[service_id][service_key]
-                if urlparse.urlparse(service.get('url')).netloc == netloc:
+                if parse.urlparse(service.get('url')).netloc == netloc:
                     selected_page = Page()
                     selected_page.id = '__root'
                     selected_page.template_name = 'standard'
diff --git a/combo/settings.py b/combo/settings.py
index 774a246a..405fcacf 100644
--- a/combo/settings.py
+++ b/combo/settings.py
@@ -369,7 +369,7 @@ LEGACY_CHART_CELL_ENABLED = False
 
 
 def debug_show_toolbar(request):
-    from debug_toolbar.middleware import show_toolbar as dt_show_toolbar
+    from debug_toolbar.middleware import show_toolbar as dt_show_toolbar  # pylint: disable=import-error
 
     return dt_show_toolbar(request) and not request.path.startswith('/__skeleton__/')
 
diff --git a/combo/urls.py b/combo/urls.py
index 69aee1ec..1a81bee0 100644
--- a/combo/urls.py
+++ b/combo/urls.py
@@ -54,7 +54,7 @@ urlpatterns += staticfiles_urlpatterns()
 urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
 
 if settings.DEBUG and 'debug_toolbar' in settings.INSTALLED_APPS:
-    import debug_toolbar
+    import debug_toolbar  # pylint: disable=import-error
 
     urlpatterns = [
         url(r'^__debug__/', include(debug_toolbar.urls)),
diff --git a/combo/utils/misc.py b/combo/utils/misc.py
index 5d31905a..b6e4aa27 100644
--- a/combo/utils/misc.py
+++ b/combo/utils/misc.py
@@ -15,11 +15,11 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import html
+from urllib import parse
 
 from django.conf import settings
 from django.template.context import BaseContext
 from django.utils.html import strip_tags
-from django.utils.six.moves.urllib import parse as urlparse
 
 
 def ellipsize(text, length=50):
@@ -42,12 +42,12 @@ def flatten_context(context):
 
 
 def is_url_from_known_service(url):
-    netloc = urlparse.urlparse(url).netloc
+    netloc = parse.urlparse(url).netloc
     if not netloc:
         return True
     for service_id in settings.KNOWN_SERVICES or {}:
         for service_key in settings.KNOWN_SERVICES[service_id]:
             service = settings.KNOWN_SERVICES[service_id][service_key]
-            if urlparse.urlparse(service.get('url')).netloc == netloc:
+            if parse.urlparse(service.get('url')).netloc == netloc:
                 return True
     return False
diff --git a/combo/utils/requests_wrapper.py b/combo/utils/requests_wrapper.py
index c7517709..d5732fbb 100644
--- a/combo/utils/requests_wrapper.py
+++ b/combo/utils/requests_wrapper.py
@@ -16,13 +16,13 @@
 
 import hashlib
 import logging
+from urllib import parse
 
 from django.conf import settings
 from django.core.cache import cache
 from django.utils.encoding import smart_bytes
 from django.utils.http import urlencode
 from django.utils.six import BytesIO
-from django.utils.six.moves.urllib import parse as urlparse
 from requests import Response
 from requests import Session as RequestsSession
 from requests.auth import AuthBase
@@ -60,11 +60,11 @@ class Requests(RequestsSession):
 
         if remote_service == 'auto':
             remote_service = None
-            scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
+            scheme, netloc, path, params, query, fragment = parse.urlparse(url)
             for services in settings.KNOWN_SERVICES.values():
                 for service in services.values():
                     remote_url = service.get('url')
-                    remote_scheme, remote_netloc, dummy, dummy, dummy, dummy = urlparse.urlparse(remote_url)
+                    remote_scheme, remote_netloc, dummy, dummy, dummy, dummy = parse.urlparse(remote_url)
                     if remote_scheme == scheme and remote_netloc == netloc:
                         remote_service = service
                         break
@@ -74,7 +74,7 @@ class Requests(RequestsSession):
             if remote_service:
                 # only keeps the path (URI) in url parameter, scheme and netloc are
                 # in remote_service
-                url = urlparse.urlunparse(('', '', path, params, query, fragment))
+                url = parse.urlunparse(('', '', path, params, query, fragment))
             else:
                 logging.warning('service not found in settings.KNOWN_SERVICES for %s', url)
 
@@ -103,7 +103,7 @@ class Requests(RequestsSession):
                 query_params['orig'] = remote_service.get('orig')
 
             remote_service_base_url = remote_service.get('url')
-            scheme, netloc, dummy, params, old_query, fragment = urlparse.urlparse(remote_service_base_url)
+            scheme, netloc, dummy, params, old_query, fragment = parse.urlparse(remote_service_base_url)
 
             query = urlencode(query_params)
             if '?' in url:
@@ -112,7 +112,7 @@ class Requests(RequestsSession):
             else:
                 path = url
 
-            url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
+            url = parse.urlunparse((scheme, netloc, path, params, query, fragment))
 
         if method == 'GET' and cache_duration:
             # handle cache
diff --git a/combo/utils/signature.py b/combo/utils/signature.py
index 03b929d9..8089d517 100644
--- a/combo/utils/signature.py
+++ b/combo/utils/signature.py
@@ -19,19 +19,19 @@ import datetime
 import hashlib
 import hmac
 import random
+from urllib import parse
 
 from django.conf import settings
 from django.utils.encoding import smart_bytes
 from django.utils.http import quote, urlencode
-from django.utils.six.moves.urllib import parse as urlparse
 
 # Simple signature scheme for query strings
 
 
 def sign_url(url, key, algo='sha256', timestamp=None, nonce=None):
-    parsed = urlparse.urlparse(url)
+    parsed = parse.urlparse(url)
     new_query = sign_query(parsed.query, key, algo, timestamp, nonce)
-    return urlparse.urlunparse(parsed[:4] + (new_query,) + parsed[5:])
+    return parse.urlunparse(parsed[:4] + (new_query,) + parsed[5:])
 
 
 def sign_query(query, key, algo='sha256', timestamp=None, nonce=None):
@@ -71,7 +71,7 @@ def check_request_signature(django_request, keys=[]):
 
 
 def check_query(query, keys, known_nonce=None, timedelta=30):
-    parsed = urlparse.parse_qs(query)
+    parsed = parse.parse_qs(query)
     if not ('signature' in parsed and 'algo' in parsed and 'timestamp' in parsed and 'nonce' in parsed):
         return False
     unsigned_query, signature_content = query.split('&signature=', 1)
diff --git a/combo/utils/spooler.py b/combo/utils/spooler.py
index 40298ccf..8ca417cd 100644
--- a/combo/utils/spooler.py
+++ b/combo/utils/spooler.py
@@ -44,8 +44,8 @@ def ensure_db(func):
 
 @contextlib.contextmanager
 def tenant_context(domain):
-    from hobo.multitenant.middleware import TenantMiddleware
-    from tenant_schemas.utils import tenant_context
+    from hobo.multitenant.middleware import TenantMiddleware  # pylint: disable=import-error
+    from tenant_schemas.utils import tenant_context  # pylint: disable=import-error
 
     tenant = TenantMiddleware.get_tenant_by_hostname(domain)
     with tenant_context(tenant):
diff --git a/combo/wsgi.py b/combo/wsgi.py
index 523eadfb..88a3dad5 100644
--- a/combo/wsgi.py
+++ b/combo/wsgi.py
@@ -19,6 +19,6 @@ if 'hobo.context_processors.statics_hash' in settings.TEMPLATES[0]['OPTIONS']['c
     # initialize versions as soon as possible as it may be used in most
     # requests via the statics_hash template var and we prefer not to
     # load the information during a user request.
-    from hobo.scrutiny.wsgi.middleware import VersionMiddleware
+    from hobo.scrutiny.wsgi.middleware import VersionMiddleware  # pylint: disable=import-error
 
     VersionMiddleware.get_packages_version()
diff --git a/tests/test_calendar.py b/tests/test_calendar.py
index a352e8e0..ccc82d0b 100644
--- a/tests/test_calendar.py
+++ b/tests/test_calendar.py
@@ -1,12 +1,12 @@
 import datetime
 import json
 from unittest import mock
+from urllib import parse
 
 import pytest
 from django.contrib.auth.models import User
 from django.core.cache import cache
 from django.urls import reverse
-from django.utils.six.moves.urllib import parse as urlparse
 
 from combo.apps.calendar.models import BookingCalendar
 from combo.apps.calendar.utils import get_calendar, get_chrono_service
@@ -215,9 +215,9 @@ def test_cell_rendering(mocked_send, client, cell, async_url):
     resp.form.set('slots', True, 1)
     resp.form.set('slots', True, 2)
     resp = resp.form.submit()
-    parsed = urlparse.urlparse(resp.url)
+    parsed = parse.urlparse(resp.url)
     assert parsed.path == '/test/'
-    qs = urlparse.parse_qs(parsed.query)
+    qs = parse.parse_qs(parsed.query)
     assert qs['session_var_booking_agenda_slug'] == ['test']
     assert qs['session_var_booking_start'] == ['2017-06-13T08:00:00+00:00']
     assert qs['session_var_booking_end'] == ['2017-06-13T09:30:00+00:00']
diff --git a/tests/test_dashboard.py b/tests/test_dashboard.py
index c57da7c4..721ed164 100644
--- a/tests/test_dashboard.py
+++ b/tests/test_dashboard.py
@@ -2,6 +2,7 @@ import datetime
 import json
 import os
 from unittest import mock
+from urllib import parse
 
 import pytest
 from django.apps import apps
@@ -9,7 +10,6 @@ from django.conf import settings
 from django.contrib.auth.models import User
 from django.test import override_settings
 from django.urls import reverse
-from django.utils.six.moves.urllib import parse as urlparse
 
 from combo.apps.dashboard.models import DashboardCell, Tile
 from combo.data.models import Page, TextCell
@@ -50,7 +50,7 @@ def test_add_to_dashboard(app, site):
     dashboard = DashboardCell.objects.all()[0]
     user = User.objects.all()[0]
     resp = app.get(reverse('combo-dashboard-add-tile', kwargs={'cell_reference': cell.get_reference()}))
-    assert urlparse.urlparse(resp.location).path == dashboard.page.get_online_url()
+    assert parse.urlparse(resp.location).path == dashboard.page.get_online_url()
     assert Tile.objects.count() == 1
     assert Tile.objects.all()[0].cell.id != cell.id
     assert Tile.objects.all()[0].cell.text == cell.text
diff --git a/tests/test_lingo_payment.py b/tests/test_lingo_payment.py
index 3614c7d0..ac24ca79 100644
--- a/tests/test_lingo_payment.py
+++ b/tests/test_lingo_payment.py
@@ -4,6 +4,7 @@ from contextlib import contextmanager
 from datetime import datetime, timedelta
 from decimal import Decimal
 from unittest import mock
+from urllib import parse
 
 import eopayment
 import httmock
@@ -16,7 +17,6 @@ from django.http.request import QueryDict
 from django.test import override_settings
 from django.urls import reverse
 from django.utils import timezone
-from django.utils.six.moves.urllib import parse as urlparse
 from django.utils.timezone import now, utc
 from mellon.models import UserSAMLIdentifier
 from requests.exceptions import ConnectionError
@@ -141,7 +141,7 @@ def assert_payment_status(url, transaction_id=None):
 
     if transaction_id:
         url, part = url.split('?')
-        query = urlparse.parse_qs(part)
+        query = parse.parse_qs(part)
         assert 'transaction-id' in query
         assert ':' not in query['transaction-id']
         assert signing_loads(query['transaction-id'][0]) == transaction_id
@@ -233,12 +233,12 @@ def test_successfull_items_payment(app, basket_page, regie, user, with_payment_b
     assert resp.status_code == 302
     location = resp.location
     assert 'dummy-payment' in location
-    parsed = urlparse.urlparse(location)
+    parsed = parse.urlparse(location)
     # get return_url and transaction id from location
-    qs = urlparse.parse_qs(parsed.query)
+    qs = parse.parse_qs(parsed.query)
     args = {'transaction_id': qs['transaction_id'][0], 'signed': True, 'ok': True, 'reason': 'Paid'}
     # make sure return url is the user return URL
-    assert urlparse.urlparse(qs['return_url'][0]).path.startswith(
+    assert parse.urlparse(qs['return_url'][0]).path.startswith(
         '/lingo/return-payment-backend/%s/' % regie.payment_backend.id
     )
     # simulate successful call to callback URL
@@ -249,10 +249,10 @@ def test_successfull_items_payment(app, basket_page, regie, user, with_payment_b
     resp = app.get(qs['return_url'][0], params=args)
     # redirect to payment status
     assert resp.status_code == 302
-    assert urlparse.urlparse(resp.url).path.startswith('/lingo/payment-status')
+    assert parse.urlparse(resp.url).path.startswith('/lingo/payment-status')
     resp = resp.follow()
     assert 'Your payment has been succesfully registered.' in resp.text
-    assert urlparse.urlparse(resp.html.find('a', {'id': 'next-url'})['href']).path == '/test_basket_cell/'
+    assert parse.urlparse(resp.html.find('a', {'id': 'next-url'})['href']).path == '/test_basket_cell/'
 
 
 def test_add_amount_to_basket(app, key, regie, user_name_id):
@@ -346,7 +346,7 @@ def test_add_amount_to_basket(app, key, regie, user_name_id):
     assert resp.status_code == 200
     response = json.loads(resp.text)
     assert response['result'] == 'success'
-    payment_url = urlparse.urlparse(response['payment_url'])
+    payment_url = parse.urlparse(response['payment_url'])
     assert payment_url.path.startswith('/lingo/item/')
     assert payment_url.path.endswith('/pay')
     assert BasketItem.objects.filter(amount=Decimal('22.23')).exists()
@@ -395,7 +395,7 @@ def test_basket_item_with_capture_date(app, user, user_name_id, regie, basket_pa
     monkeypatch.setattr(eopayment.Payment, 'request', eopayment_mock)
     resp = resp.form.submit()
     assert resp.status_code == 302
-    location = urlparse.urlparse(resp.location)
+    location = parse.urlparse(resp.location)
     assert location.path == '/'
     assert location.hostname == 'dummy-payment.demo.entrouvert.com'
 
@@ -527,7 +527,7 @@ def test_cant_pay_if_different_capture_date(app, basket_page, regie, user):
     resp = login(app).get('/test_basket_cell/')
     resp = resp.form.submit()
     assert resp.status_code == 302
-    assert urlparse.urlparse(resp.location).path == '/test_basket_cell/'
+    assert parse.urlparse(resp.location).path == '/test_basket_cell/'
     resp = resp.follow()
     assert "Invalid grouping for basket items: different capture dates." in resp.text
 
@@ -572,7 +572,7 @@ def test_pay_single_basket_item(app, key, regie, user_name_id, john_doe):
 
     # make sure the redirection is done to the payment backend
     assert resp.location.startswith('http://dummy-payment.demo.entrouvert.com/')
-    qs = urlparse.parse_qs(urlparse.urlparse(resp.location).query)
+    qs = parse.parse_qs(parse.urlparse(resp.location).query)
     assert qs['amount'] == ['12.00']
     # simulate successful payment response from dummy backend
     data = {'transaction_id': qs['transaction_id'][0], 'ok': True, 'amount': qs['amount'][0], 'signed': True}
@@ -610,7 +610,7 @@ def test_pay_single_basket_item_another_user(app, key, regie, user_name_id, john
 
     # make sure the redirection is done to the payment backend
     assert resp.location.startswith('http://dummy-payment.demo.entrouvert.com/')
-    qs = urlparse.parse_qs(urlparse.urlparse(resp.location).query)
+    qs = parse.parse_qs(parse.urlparse(resp.location).query)
     assert qs['amount'] == ['12.00']
     # simulate successful payment response from dummy backend
     data = {'transaction_id': qs['transaction_id'][0], 'ok': True, 'amount': qs['amount'][0], 'signed': True}
@@ -635,12 +635,12 @@ def test_pay_multiple_regies(app, key, regie, user_name_id):
     resp = login(app).get(page.get_online_url())
     resp = resp.forms[0].submit()
     assert resp.location.startswith('http://dummy-payment.demo.entrouvert.com/')
-    qs = urlparse.parse_qs(urlparse.urlparse(resp.location).query)
+    qs = parse.parse_qs(parse.urlparse(resp.location).query)
     assert qs['amount'] == ['234.46']
 
     resp = login(app).get(page.get_online_url())
     resp = resp.forms[1].submit()
-    qs = urlparse.parse_qs(urlparse.urlparse(resp.location).query)
+    qs = parse.parse_qs(parse.urlparse(resp.location).query)
     assert qs['amount'] == ['22.23']
 
 
@@ -816,8 +816,8 @@ def test_payment_callback(app, basket_page, regie, user, with_payment_backend):
     resp = resp.form.submit()
     assert resp.status_code == 302
     location = resp.location
-    parsed = urlparse.urlparse(location)
-    qs = urlparse.parse_qs(parsed.query)
+    parsed = parse.urlparse(location)
+    qs = parse.parse_qs(parsed.query)
     transaction_id = qs['transaction_id'][0]
     data = {'transaction_id': transaction_id, 'signed': True, 'amount': qs['amount'][0], 'ok': True}
     assert data['amount'] == '10.50'
@@ -838,8 +838,8 @@ def test_payment_callback(app, basket_page, regie, user, with_payment_backend):
     resp = resp.form.submit()
     assert resp.status_code == 302
     location = resp.location
-    parsed = urlparse.urlparse(location)
-    qs = urlparse.parse_qs(parsed.query)
+    parsed = parse.urlparse(location)
+    qs = parse.parse_qs(parsed.query)
     transaction_id = qs['transaction_id'][0]
     return_url = qs['return_url'][0]
     data = {'transaction_id': transaction_id, 'signed': True, 'amount': qs['amount'][0], 'ok': True}
@@ -867,8 +867,8 @@ def test_payment_callback_no_regie(app, basket_page, regie, user, with_payment_b
     resp = resp.form.submit()
     assert resp.status_code == 302
     location = resp.location
-    parsed = urlparse.urlparse(location)
-    qs = urlparse.parse_qs(parsed.query)
+    parsed = parse.urlparse(location)
+    qs = parse.parse_qs(parsed.query)
     transaction_id = qs['transaction_id'][0]
     data = {'transaction_id': transaction_id, 'signed': True, 'amount': qs['amount'][0], 'ok': True}
     assert data['amount'] == '10.50'
@@ -889,8 +889,8 @@ def test_payment_callback_no_regie(app, basket_page, regie, user, with_payment_b
     resp = resp.form.submit()
     assert resp.status_code == 302
     location = resp.location
-    parsed = urlparse.urlparse(location)
-    qs = urlparse.parse_qs(parsed.query)
+    parsed = parse.urlparse(location)
+    qs = parse.parse_qs(parsed.query)
     transaction_id = qs['transaction_id'][0]
     data = {'transaction_id': transaction_id, 'signed': True, 'amount': qs['amount'][0], 'ok': True}
     assert data['amount'] == '11.50'
@@ -907,8 +907,8 @@ def test_payment_return_without_query_string(app, basket_page, regie, user, with
     resp = resp.form.submit()
     assert resp.status_code == 302
     location = resp.location
-    parsed = urlparse.urlparse(location)
-    qs = urlparse.parse_qs(parsed.query)
+    parsed = parse.urlparse(location)
+    qs = parse.parse_qs(parsed.query)
     return_url = qs['return_url'][0]
     transaction_id = qs['transaction_id'][0]
     data = {'transaction_id': transaction_id, 'signed': True, 'amount': qs['amount'][0], 'ok': True}
@@ -957,8 +957,8 @@ def test_payment_callback_waiting(app, basket_page, regie, user, with_payment_ba
     resp = resp.form.submit()
     assert resp.status_code == 302
     location = resp.location
-    parsed = urlparse.urlparse(location)
-    qs = urlparse.parse_qs(parsed.query)
+    parsed = parse.urlparse(location)
+    qs = parse.parse_qs(parsed.query)
     transaction_id = qs['transaction_id'][0]
     data = {'transaction_id': transaction_id, 'signed': True, 'amount': qs['amount'][0], 'waiting': True}
     assert data['amount'] == '10.50'
@@ -994,8 +994,8 @@ def test_payment_no_callback_just_return(caplog, app, basket_page, regie, user,
     resp = resp.form.submit()
     assert resp.status_code == 302
     location = resp.location
-    parsed = urlparse.urlparse(location)
-    qs = urlparse.parse_qs(parsed.query)
+    parsed = parse.urlparse(location)
+    qs = parse.parse_qs(parsed.query)
     transaction_id = qs['transaction_id'][0]
     data = {'transaction_id': transaction_id, 'amount': qs['amount'][0], 'ok': True}
     assert data['amount'] == '10.50'
@@ -1007,7 +1007,7 @@ def test_payment_no_callback_just_return(caplog, app, basket_page, regie, user,
             get_resp = app.post(return_url, params=data)
             assert request.call_count == 0
         assert get_resp.status_code == 302
-        assert urlparse.urlparse(get_resp['location']).path == '/lingo/payment-status'
+        assert parse.urlparse(get_resp['location']).path == '/lingo/payment-status'
         assert Transaction.objects.get(order_id=transaction_id).status == 0  # not paid
 
     # call return with missing data
@@ -1030,10 +1030,10 @@ def test_payment_no_callback_just_return(caplog, app, basket_page, regie, user,
         assert url.startswith('http://example.org/testitem/jump/trigger/paid')
     # redirect to payment status
     assert get_resp.status_code == 302
-    assert urlparse.urlparse(get_resp.url).path.startswith('/lingo/payment-status')
+    assert parse.urlparse(get_resp.url).path.startswith('/lingo/payment-status')
     resp = get_resp.follow()
     assert 'Your payment has been succesfully registered.' in resp.text
-    assert urlparse.urlparse(resp.html.find('a', {'id': 'next-url'})['href']).path == '/test_basket_cell/'
+    assert parse.urlparse(resp.html.find('a', {'id': 'next-url'})['href']).path == '/test_basket_cell/'
     assert Transaction.objects.get(order_id=transaction_id).status == eopayment.PAID
 
 
@@ -1353,8 +1353,8 @@ def test_extra_fees(app, basket_page, key, regie, user_name_id):
         resp = resp.form.submit()
     assert resp.status_code == 302
     location = resp.location
-    parsed = urlparse.urlparse(location)
-    qs = urlparse.parse_qs(parsed.query)
+    parsed = parse.urlparse(location)
+    qs = parse.parse_qs(parsed.query)
     transaction_id = qs['transaction_id'][0]
     data = {'transaction_id': transaction_id, 'signed': True, 'amount': qs['amount'][0], 'ok': True}
     assert data['amount'] == '44.00'
@@ -1369,8 +1369,8 @@ def test_extra_fees(app, basket_page, key, regie, user_name_id):
         resp = resp.form.submit()
     assert resp.status_code == 302
     location = resp.location
-    parsed = urlparse.urlparse(location)
-    qs = urlparse.parse_qs(parsed.query)
+    parsed = parse.urlparse(location)
+    qs = parse.parse_qs(parsed.query)
     transaction_id = qs['transaction_id'][0]
     data = {'transaction_id': transaction_id, 'signed': True, 'amount': qs['amount'][0], 'ok': True}
     assert data['amount'] == '45.00'
@@ -1391,8 +1391,8 @@ def test_payment_callback_error(app, basket_page, regie, user, with_payment_back
     resp = resp.form.submit()
     assert resp.status_code == 302
     location = resp.location
-    parsed = urlparse.urlparse(location)
-    qs = urlparse.parse_qs(parsed.query)
+    parsed = parse.urlparse(location)
+    qs = parse.parse_qs(parsed.query)
     transaction_id = qs['transaction_id'][0]
     data = {'transaction_id': transaction_id, 'signed': True, 'amount': qs['amount'][0], 'ok': True}
     assert data['amount'] == '10.50'
@@ -1463,7 +1463,7 @@ def test_payment_no_basket(app, user_name_id, regie, authenticated):
     item = BasketItem.objects.first()
     assert item.user is None
     assert item.amount == Decimal('10.00')
-    path = urlparse.urlparse(payment_url).path
+    path = parse.urlparse(payment_url).path
     start = '/lingo/item/'
     end = '/pay'
     assert path.startswith(start)
@@ -1489,7 +1489,7 @@ def test_payment_no_basket(app, user_name_id, regie, authenticated):
     item.save()
     resp = app.get(payment_url)
     assert resp.location.startswith('http://dummy-payment.demo.entrouvert.com/')
-    qs = urlparse.parse_qs(urlparse.urlparse(resp.location).query)
+    qs = parse.parse_qs(parse.urlparse(resp.location).query)
     assert qs['amount'] == ['10.00']
     if authenticated:
         assert qs['email'] == ['foo@example.com']
@@ -1504,7 +1504,7 @@ def test_payment_no_basket(app, user_name_id, regie, authenticated):
         },
     )
     assert resp.location.startswith('http://dummy-payment.demo.entrouvert.com/')
-    qs = urlparse.parse_qs(urlparse.urlparse(resp.location).query)
+    qs = parse.parse_qs(parse.urlparse(resp.location).query)
     assert qs['amount'] == ['10.00']
     if authenticated:
         assert qs['email'] == ['foo@example.com']
@@ -1644,7 +1644,7 @@ def test_bank_transaction_date(app, key, regie, user, john_doe, caplog, transact
 
     # make sure the redirection is done to the payment backend
     assert resp.location.startswith('http://dummy-payment.demo.entrouvert.com/')
-    qs = urlparse.parse_qs(urlparse.urlparse(resp.location).query)
+    qs = parse.parse_qs(parse.urlparse(resp.location).query)
     assert qs['amount'] == ['12.00']
     # simulate successful payment response from dummy backend
     transaction_id = (qs['transaction_id'][0],)
@@ -1722,7 +1722,7 @@ def test_successfull_items_can_pay_only_one_basket_item(app, basket_page, mono_r
     resp = resp.click('Pay', href=item.payment_url)
 
     # successful payment
-    qs = urlparse.parse_qs(urlparse.urlparse(resp.location).query)
+    qs = parse.parse_qs(parse.urlparse(resp.location).query)
     assert qs['subject'][0] == 'foo item', 'item.subject was not provided to eopayment'
     assert qs['orderid'][0] == 'form-3-23'
     args = {'transaction_id': qs['transaction_id'][0], 'signed': True, 'ok': True, 'reason': 'Paid'}
@@ -1744,7 +1744,7 @@ def test_extra_kwargs_can_pay_only_one_basket_item(app, basket_page, mono_regie,
     )
 
     resp = login(app).get(item.payment_url)
-    qs = urlparse.parse_qs(urlparse.urlparse(resp.location).query)
+    qs = parse.parse_qs(parse.urlparse(resp.location).query)
     assert qs['subject'][0] == 'foo item', 'item.subject was not provided to eopayment'
     assert qs['info3'][0] == 'très fragile'
 
@@ -1789,7 +1789,7 @@ def test_tipi_kwargs_can_pay_only_one_basket_item(app, basket_page, mono_regie,
     mono_regie.payment_backend.save()
 
     resp = login(app).get(item.payment_url)
-    qs = urlparse.parse_qs(urlparse.urlparse(resp.location).query)
+    qs = parse.parse_qs(parse.urlparse(resp.location).query)
     assert qs['refdet'][0] == 'F20201030'
     assert qs['exer'][0] == '2020'
 
@@ -1836,7 +1836,7 @@ def remote_invoices_httmock():
 
 def test_email_from_basket(app, regie, remote_invoices_httmock):
     def parse_qs(url):
-        return QueryDict(urlparse.urlparse(url).query)
+        return QueryDict(parse.urlparse(url).query)
 
     user1 = User.objects.create(username='user1', email='user1@example.com')
     user2 = User.objects.create(username='user2', email='user2@example.com')
diff --git a/tests/test_lingo_remote_regie.py b/tests/test_lingo_remote_regie.py
index 050ae850..d45ed0db 100644
--- a/tests/test_lingo_remote_regie.py
+++ b/tests/test_lingo_remote_regie.py
@@ -2,6 +2,7 @@ import copy
 import json
 from decimal import Decimal
 from unittest import mock
+from urllib import parse
 
 import eopayment
 import httmock
@@ -14,7 +15,6 @@ from django.test import override_settings
 from django.test.client import RequestFactory
 from django.urls import reverse
 from django.utils.encoding import force_bytes, force_text
-from django.utils.six.moves.urllib import parse as urlparse
 from django.utils.timezone import now, timedelta
 from requests.exceptions import ConnectionError
 from requests.models import Response
@@ -156,11 +156,11 @@ def test_remote_regie_active_invoices_cell(mock_send, remote_regie):
     # check if regie webservice has been correctly called
     assert mock_send.call_args[0][0].method == 'GET'
     url = mock_send.call_args[0][0].url
-    scheme, netloc, path, dummy, querystring, dummy = urlparse.urlparse(url)
+    scheme, netloc, path, dummy, querystring, dummy = parse.urlparse(url)
     assert scheme == 'http'
     assert netloc == 'example.org'
     assert path == '/regie/invoices/'
-    query = urlparse.parse_qs(querystring, keep_blank_values=True)
+    query = parse.parse_qs(querystring, keep_blank_values=True)
     assert query['NameID'][0] == 'r2d2'
     assert query['orig'][0] == 'combo'
     assert check_query(querystring, 'combo') is True
@@ -285,11 +285,11 @@ def test_remote_regie_past_invoices_cell(mock_send, remote_regie):
     # check if regie webservice has been correctly called
     assert mock_send.call_args[0][0].method == 'GET'
     url = mock_send.call_args[0][0].url
-    scheme, netloc, path, dummy, querystring, dummy = urlparse.urlparse(url)
+    scheme, netloc, path, dummy, querystring, dummy = parse.urlparse(url)
     assert scheme == 'http'
     assert netloc == 'example.org'
     assert path == '/regie/invoices/history/'
-    query = urlparse.parse_qs(querystring, keep_blank_values=True)
+    query = parse.parse_qs(querystring, keep_blank_values=True)
     assert query['NameID'][0] == 'r2d2'
     assert query['orig'][0] == 'combo'
     assert check_query(querystring, 'combo') is True
@@ -408,20 +408,20 @@ def test_anonymous_successful_item_payment(mock_get, mock_pay_invoice, app, remo
     assert resp.status_code == 302
     location = resp.location
     assert 'dummy-payment' in location
-    parsed = urlparse.urlparse(location)
+    parsed = parse.urlparse(location)
     # get return_url and transaction id from location
-    qs = urlparse.parse_qs(parsed.query)
+    qs = parse.parse_qs(parsed.query)
     args = {'transaction_id': qs['transaction_id'][0], 'signed': True, 'ok': True, 'reason': 'Paid'}
     # make sure return url is the user return URL
     return_url = qs['return_url'][0]
-    assert urlparse.urlparse(return_url).path.startswith('/lingo/return-payment-backend')
+    assert parse.urlparse(return_url).path.startswith('/lingo/return-payment-backend')
     # simulate successful return URL
     resp = app.get(qs['return_url'][0], params=args)
     # redirect to payment status
     assert resp.status_code == 302
-    assert urlparse.urlparse(resp.url).path.startswith('/lingo/payment-status')
+    assert parse.urlparse(resp.url).path.startswith('/lingo/payment-status')
     resp = resp.follow()
-    assert urlparse.urlparse(resp.html.find('a', {'id': 'next-url'})['href']).path == '/'
+    assert parse.urlparse(resp.html.find('a', {'id': 'next-url'})['href']).path == '/'
     # simulate successful call to callback URL
     resp = app.get(reverse('lingo-callback', kwargs={'regie_pk': remote_regie.id}), params=args)
     trans = Transaction.objects.all()
@@ -520,7 +520,7 @@ def test_anonymous_item_payment_email_error(mock_get, app, remote_regie):
     resp = form.submit()
 
     assert resp.status_code == 302
-    path = urlparse.urlparse(resp.location).path
+    path = parse.urlparse(resp.location).path
     assert path == '/lingo/item/%s/%s/' % (remote_regie.id, encrypt_id)
 
 
@@ -570,7 +570,7 @@ def test_self_declared_invoice(mock_get, app, remote_regie):
     resp.form['invoice-number'] = 'F201601'
     resp.form['invoice-amount'] = '123.45'
     resp = resp.form.submit()
-    path = urlparse.urlparse(resp.location).path
+    path = parse.urlparse(resp.location).path
     assert path.startswith('/lingo/item/%s/' % remote_regie.id)
     resp = resp.follow()
 
@@ -605,13 +605,13 @@ def test_remote_item_payment_failure(mock_post, mock_get, mock_pay_invoice, app,
     assert resp.status_code == 302
     location = resp.location
     assert 'dummy-payment' in location
-    parsed = urlparse.urlparse(location)
+    parsed = parse.urlparse(location)
     # get return_url and transaction id from location
-    qs = urlparse.parse_qs(parsed.query)
+    qs = parse.parse_qs(parsed.query)
     args = {'transaction_id': qs['transaction_id'][0], 'signed': True, 'ok': True, 'reason': 'Paid'}
     # make sure return url is the user return URL
     return_url = qs['return_url'][0]
-    assert urlparse.urlparse(return_url).path.startswith(
+    assert parse.urlparse(return_url).path.startswith(
         '/lingo/return-payment-backend/%s/' % remote_regie.payment_backend.id
     )
     # simulate payment failure
@@ -619,10 +619,10 @@ def test_remote_item_payment_failure(mock_post, mock_get, mock_pay_invoice, app,
     resp = app.get(qs['return_url'][0], params=args)
     # redirect to payment status
     assert resp.status_code == 302
-    assert urlparse.urlparse(resp.url).path.startswith('/lingo/payment-status')
+    assert parse.urlparse(resp.url).path.startswith('/lingo/payment-status')
     resp = resp.follow()
     assert (
-        urlparse.urlparse(resp.html.find('a', {'id': 'next-url'})['href']).path
+        parse.urlparse(resp.html.find('a', {'id': 'next-url'})['href']).path
         == '/active-remote-invoices-page/'
     )
 
@@ -674,9 +674,9 @@ def test_remote_invoice_successfull_payment_redirect(
     assert resp.status_code == 302
     location = resp.location
     assert 'dummy-payment' in location
-    parsed = urlparse.urlparse(location)
+    parsed = parse.urlparse(location)
     # get return_url and transaction id from location
-    qs = urlparse.parse_qs(parsed.query)
+    qs = parse.parse_qs(parsed.query)
     if can_pay_only_one_basket_item:
         assert qs['orderid'] == ['order-id-1']
         assert qs['subject'] == ['invoice-one']
@@ -687,10 +687,10 @@ def test_remote_invoice_successfull_payment_redirect(
     resp = app.get(qs['return_url'][0], params=args)
     # redirect to payment status
     assert resp.status_code == 302
-    assert urlparse.urlparse(resp.url).path.startswith('/lingo/payment-status')
+    assert parse.urlparse(resp.url).path.startswith('/lingo/payment-status')
     resp = resp.follow()
     assert (
-        urlparse.urlparse(resp.html.find('a', {'id': 'next-url'})['href']).path
+        parse.urlparse(resp.html.find('a', {'id': 'next-url'})['href']).path
         == '/active-remote-invoices-page/'
     )
 
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 446f0a94..ffa84ee2 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -5,6 +5,7 @@ import os
 import re
 import shutil
 from unittest import mock
+from urllib import parse
 
 import pytest
 from django.conf import settings
@@ -21,7 +22,6 @@ from django.urls import reverse
 from django.utils.encoding import force_bytes, force_str
 from django.utils.http import urlencode
 from django.utils.six import BytesIO
-from django.utils.six.moves.urllib import parse as urlparse
 from django.utils.timezone import now
 from webtest import Upload
 
@@ -2369,7 +2369,7 @@ def test_django_admin(app, admin_user):
     assert '/admin/logout/' in resp.text
     resp = resp.click(href='/admin/logout/')
     resp = resp.follow()  # -> /logout/
-    assert urlparse.urlparse(resp.location).path == '/'
+    assert parse.urlparse(resp.location).path == '/'
 
 
 def test_json_cell_syntax_validation(app, admin_user):
diff --git a/tests/test_public.py b/tests/test_public.py
index 9eaa11ef..66e9ef88 100644
--- a/tests/test_public.py
+++ b/tests/test_public.py
@@ -3,6 +3,7 @@ import json
 import os
 import re
 from unittest import mock
+from urllib import parse
 
 import pytest
 from django.conf import settings
@@ -14,7 +15,6 @@ from django.test.utils import CaptureQueriesContext
 from django.urls import reverse
 from django.utils.http import quote
 from django.utils.six import StringIO
-from django.utils.six.moves.urllib import parse as urlparse
 
 try:
     import mellon  # pylint: disable=unused-import
@@ -52,7 +52,7 @@ def test_index(app):
     page = Page(title='Home', slug='index', template_name='standard')
     page.save()
     resp = app.get('/index', status=301)
-    assert urlparse.urlparse(resp.location).path == '/'
+    assert parse.urlparse(resp.location).path == '/'
     resp = app.get('/', status=200)
 
     # check {% now %} inside a skeleton_extra_placeholder is interpreted
@@ -88,9 +88,9 @@ def test_mellon_login(app):
     with mock.patch('combo.public.views.get_idps') as get_idps:
         get_idps.return_value = ['xxx']
         resp = app.get('/login/')
-        assert urlparse.urlparse(resp.location).path == '/accounts/mellon/login/'
+        assert parse.urlparse(resp.location).path == '/accounts/mellon/login/'
         resp = app.get('/login/?next=whatever')
-        assert urlparse.urlparse(resp.location).query == 'next=whatever'
+        assert parse.urlparse(resp.location).query == 'next=whatever'
 
 
 def test_page_contents_group_presence(app, normal_user):
@@ -158,7 +158,7 @@ def test_page_footer_acquisition(app):
     ParentContentCell(page=page, placeholder='footer', order=0).save()
     TextCell(page=page, placeholder='footer', text='BAR2FOO', order=1).save()
     resp = app.get('/second', status=301)
-    assert urlparse.urlparse(resp.location).path == '/second/'
+    assert parse.urlparse(resp.location).path == '/second/'
     with CaptureQueriesContext(connection) as ctx:
         resp = app.get('/second/', status=200)
         assert resp.text.count('BARFOO') == 1
@@ -447,7 +447,7 @@ def test_subpage_location(app):
     assert 'Grand child of home' in resp.text
 
     assert (
-        urlparse.urlparse(app.get('/child-home/grand-child-home', status=301).location).path
+        parse.urlparse(app.get('/child-home/grand-child-home', status=301).location).path
         == '/child-home/grand-child-home/'
     )
     app.get('/grand-child-home/', status=404)
@@ -653,7 +653,7 @@ def test_initial_login_page(app, admin_user):
         # first visit
         app = login(app)
         resp = app.get('/', status=302)
-        assert urlparse.urlparse(resp.location).path == '/initial-login/'
+        assert parse.urlparse(resp.location).path == '/initial-login/'
 
         # visit again
         resp = app.get('/', status=200)
@@ -669,13 +669,13 @@ def test_welcome_page(app, admin_user):
     with override_settings(COMBO_WELCOME_PAGE_PATH='/welcome/'):
         app.cookiejar.clear()
         resp = app.get('/', status=302)
-        assert urlparse.urlparse(resp.location).path == '/welcome/'
+        assert parse.urlparse(resp.location).path == '/welcome/'
 
         resp = app.get('/', status=200)
 
         app.cookiejar.clear()
         resp = app.get('/', status=302)
-        assert urlparse.urlparse(resp.location).path == '/welcome/'
+        assert parse.urlparse(resp.location).path == '/welcome/'
 
         app.cookiejar.clear()
         app = login(app)
@@ -749,7 +749,7 @@ def test_post_cell(app):
                 assert requests_post.call_args[0][0] == 'POST'
                 assert requests_post.call_args[0][1] == 'http://test-post-cell/slug/create/'
                 assert requests_post.call_args[1]['json'] == {'value': 'plop', 'items': ['1']}
-                assert urlparse.urlparse(resp2.location).path == '/'
+                assert parse.urlparse(resp2.location).path == '/'
 
             # check ajax call
             with mock.patch('combo.utils.requests.request') as requests_post:
@@ -764,7 +764,7 @@ def test_post_cell(app):
             with mock.patch('combo.utils.requests.request') as requests_post:
                 requests_post.return_value = mock.Mock(content=json.dumps({'err': 0}), status_code=400)
                 resp2 = resp.form.submit()
-                assert urlparse.urlparse(resp2.location).path == '/'
+                assert parse.urlparse(resp2.location).path == '/'
                 resp2 = resp2.follow()
                 assert 'Error sending data.' in resp2.text
 
@@ -772,7 +772,7 @@ def test_post_cell(app):
                     'error-message'
                 ] = 'Failed to create stuff.'
                 resp2 = resp.form.submit()
-                assert urlparse.urlparse(resp2.location).path == '/'
+                assert parse.urlparse(resp2.location).path == '/'
                 resp2 = resp2.follow()
                 assert 'Failed to create stuff.' in resp2.text
 
@@ -799,7 +799,7 @@ def test_post_cell(app):
                 assert requests_patch.call_args[0][0] == 'PATCH'
                 assert requests_patch.call_args[0][1] == 'http://test-post-cell/slug/update/'
                 assert requests_patch.call_args[1]['json'] == {'value': 'plop', 'items': ['1']}
-                assert urlparse.urlparse(resp2.location).path == '/'
+                assert parse.urlparse(resp2.location).path == '/'
 
             # check raw result
             with mock.patch('combo.utils.requests.request') as requests_search:
@@ -1016,31 +1016,31 @@ def test_redirects(app):
     redirect = Redirect(old_url='/whatever/', page=page3)
     redirect.save()
 
-    assert urlparse.urlparse(app.get('/whatever/', status=302).location).path == '/second/third/'
-    assert urlparse.urlparse(app.get('/whatever', status=301).location).path == '/whatever/'
+    assert parse.urlparse(app.get('/whatever/', status=302).location).path == '/second/third/'
+    assert parse.urlparse(app.get('/whatever', status=301).location).path == '/whatever/'
 
     # check the most recent redirect is called
     redirect = Redirect(old_url='/whatever/', page=page2)
     redirect.save()
-    assert urlparse.urlparse(app.get('/whatever/', status=302).location).path == '/second/'
+    assert parse.urlparse(app.get('/whatever/', status=302).location).path == '/second/'
 
     # rename page
     page3.slug = 'third2'
     page3.save()
     assert app.get('/second/third2/', status=200)
-    assert urlparse.urlparse(app.get('/second/third/', status=302).location).path == '/second/third2/'
+    assert parse.urlparse(app.get('/second/third/', status=302).location).path == '/second/third2/'
 
     page2.slug = 'second2'
     page2.save()
-    assert urlparse.urlparse(app.get('/second/third/', status=302).location).path == '/second2/third2/'
-    assert urlparse.urlparse(app.get('/second/third2/', status=302).location).path == '/second2/third2/'
-    assert urlparse.urlparse(app.get('/second/', status=302).location).path == '/second2/'
+    assert parse.urlparse(app.get('/second/third/', status=302).location).path == '/second2/third2/'
+    assert parse.urlparse(app.get('/second/third2/', status=302).location).path == '/second2/third2/'
+    assert parse.urlparse(app.get('/second/', status=302).location).path == '/second2/'
 
     # change parent
     page3.parent = None
     page3.save()
-    assert urlparse.urlparse(app.get('/second/third/', status=302).location).path == '/third2/'
-    assert urlparse.urlparse(app.get('/second2/third2/', status=302).location).path == '/third2/'
+    assert parse.urlparse(app.get('/second/third/', status=302).location).path == '/third2/'
+    assert parse.urlparse(app.get('/second2/third2/', status=302).location).path == '/third2/'
 
 
 def test_sub_slug(app, john_doe, jane_doe):
@@ -1158,10 +1158,10 @@ def test_cell_slugs(app):
 
 def test_missing_trailing_slashes(app):
     # redirect to path with slash
-    assert urlparse.urlparse(app.get('/login', status=301).location).path == '/login/'
-    assert urlparse.urlparse(app.get('/foo', status=301).location).path == '/foo/'
+    assert parse.urlparse(app.get('/login', status=301).location).path == '/login/'
+    assert parse.urlparse(app.get('/foo', status=301).location).path == '/foo/'
     # don't be tricked by double slashes
-    assert urlparse.urlparse(app.get('//foo', status=301).location).path == '/foo/'
+    assert parse.urlparse(app.get('//foo', status=301).location).path == '/foo/'
 
 
 def test_cell_asset_css_classes(settings, app, admin_user):
diff --git a/tests/test_requests.py b/tests/test_requests.py
index 943879ce..0f1a2689 100644
--- a/tests/test_requests.py
+++ b/tests/test_requests.py
@@ -1,8 +1,8 @@
 from unittest import mock
+from urllib import parse
 
 import pytest
 from django.contrib.auth.models import AnonymousUser
-from django.utils.six.moves.urllib import parse as urlparse
 
 from combo.utils import NothingInCacheException, check_query, requests
 
@@ -32,8 +32,8 @@ def test_sign():
         requests.get('/foo/bar/', remote_service=remote_service)
         url = send.call_args[0][0].url
         assert url.startswith('http://example.org/foo/bar/?')
-        dummy, dummy, dummy, dummy, querystring, dummy = urlparse.urlparse(url)
-        query = urlparse.parse_qs(querystring, keep_blank_values=True)
+        dummy, dummy, dummy, dummy, querystring, dummy = parse.urlparse(url)
+        query = parse.parse_qs(querystring, keep_blank_values=True)
         assert query['orig'][0] == 'myself'
         assert query['email'][0] == ''
         assert query['NameID'][0] == ''
@@ -42,8 +42,8 @@ def test_sign():
         requests.get('/foo/bar/', remote_service=remote_service, without_user=True)
         url = send.call_args[0][0].url
         assert url.startswith('http://example.org/foo/bar/?')
-        dummy, dummy, dummy, dummy, querystring, dummy = urlparse.urlparse(url)
-        query = urlparse.parse_qs(querystring, keep_blank_values=True)
+        dummy, dummy, dummy, dummy, querystring, dummy = parse.urlparse(url)
+        query = parse.parse_qs(querystring, keep_blank_values=True)
         assert query['orig'][0] == 'myself'
         assert 'email' not in query
         assert 'NameID' not in query
@@ -55,8 +55,8 @@ def test_auto_sign():
         requests.get('http://example.org/foo/bar/', remote_service='auto')
         url = send.call_args[0][0].url
         assert url.startswith('http://example.org/foo/bar/?')
-        dummy, dummy, dummy, dummy, querystring, dummy = urlparse.urlparse(url)
-        query = urlparse.parse_qs(querystring, keep_blank_values=True)
+        dummy, dummy, dummy, dummy, querystring, dummy = parse.urlparse(url)
+        query = parse.parse_qs(querystring, keep_blank_values=True)
         assert query['orig'][0] == 'combo'
         assert check_query(querystring, 'combo') is True
 
@@ -73,8 +73,8 @@ def test_sign_user():
         requests.get('/foo/bar/', remote_service=remote_service, user=user)
         url = send.call_args[0][0].url
         assert url.startswith('http://example.org/foo/bar/?')
-        dummy, dummy, dummy, dummy, querystring, dummy = urlparse.urlparse(url)
-        query = urlparse.parse_qs(querystring, keep_blank_values=True)
+        dummy, dummy, dummy, dummy, querystring, dummy = parse.urlparse(url)
+        query = parse.parse_qs(querystring, keep_blank_values=True)
         assert query['NameID'][0] == 'r2d2'
         assert 'email' not in query
         assert query['orig'][0] == 'myself'
@@ -83,8 +83,8 @@ def test_sign_user():
         requests.get('/foo/bar/', remote_service=remote_service, user=user, federation_key='email')
         url = send.call_args[0][0].url
         assert url.startswith('http://example.org/foo/bar/?')
-        dummy, dummy, dummy, dummy, querystring, dummy = urlparse.urlparse(url)
-        query = urlparse.parse_qs(querystring, keep_blank_values=True)
+        dummy, dummy, dummy, dummy, querystring, dummy = parse.urlparse(url)
+        query = parse.parse_qs(querystring, keep_blank_values=True)
         assert query['email'][0] == 'foo@example.net'
         assert 'NameID' not in query
         assert query['orig'][0] == 'myself'
@@ -95,8 +95,8 @@ def test_sign_user():
         requests.get('/foo/bar/', remote_service=remote_service, user=user)
         url = send.call_args[0][0].url
         assert url.startswith('http://example.org/foo/bar/?')
-        dummy, dummy, dummy, dummy, querystring, dummy = urlparse.urlparse(url)
-        query = urlparse.parse_qs(querystring, keep_blank_values=True)
+        dummy, dummy, dummy, dummy, querystring, dummy = parse.urlparse(url)
+        query = parse.parse_qs(querystring, keep_blank_values=True)
         assert 'NameID' not in query
         assert query['email'][0] == 'foo@example.net'
         assert query['orig'][0] == 'myself'
@@ -112,8 +112,8 @@ def test_sign_anonymous_user():
         requests.get('/foo/bar/', remote_service=remote_service, user=user)
         url = send.call_args[0][0].url
         assert url.startswith('http://example.org/foo/bar/?')
-        dummy, dummy, dummy, dummy, querystring, dummy = urlparse.urlparse(url)
-        query = urlparse.parse_qs(querystring, keep_blank_values=True)
+        dummy, dummy, dummy, dummy, querystring, dummy = parse.urlparse(url)
+        query = parse.parse_qs(querystring, keep_blank_values=True)
         assert query['NameID'][0] == ''
         assert query['email'][0] == ''
         assert query['orig'][0] == 'myself'
diff --git a/tests/test_wcs.py b/tests/test_wcs.py
index 6d60d478..e44523ec 100644
--- a/tests/test_wcs.py
+++ b/tests/test_wcs.py
@@ -2,6 +2,7 @@ import copy
 import json
 import re
 from unittest import mock
+from urllib import parse
 
 import pytest
 from django.apps import apps
@@ -12,7 +13,6 @@ from django.db import connection
 from django.test.client import RequestFactory
 from django.test.utils import CaptureQueriesContext
 from django.urls import reverse
-from django.utils.six.moves.urllib import parse as urlparse
 from pyquery import PyQuery
 from requests.exceptions import ConnectionError
 from requests.models import Response
@@ -262,7 +262,7 @@ def get_data_from_url(url):
 
 
 def mocked_requests_send(request, **kwargs):
-    request_url = urlparse.urlparse(request.url)
+    request_url = parse.urlparse(request.url)
     data = copy.deepcopy(get_data_from_url(request_url.path))
 
     if not isinstance(data, list):
-- 
2.30.2

