Project

General

Profile

Development #28276

Fonctionner avec Python3 pour Django1.11

Added by Paul Marillonnet 8 months ago. Updated 4 months ago.

Status:
En cours
Priority:
Normal
Assignee:
-
Category:
-
Target version:
-
Start date:
23 Nov 2018
Due date:
% Done:

0%

Patch proposed:
No
Planning:
No

Description

En se basant sur le travail déjà effectué dans d'autres briques.


Related issues

Related to Authentic 2 - Development #28277: Fonctionner en Python3 pour Django1.11 : fournir un jeu de tests unitaires Information nécessaire 23 Nov 2018
Related to Authentic 2 - Development #28278: Fichier setup.py compatible Python3 Solution déployée 23 Nov 2018
Related to Authentic 2 - Development #31137: python3: adapter la syntaxe des exceptions Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31139: python3: utiliser les fonctions de parsing d'url de django.utils.six Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31140: python3: utiliser __future__.print_function Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31143: python3 : corriger les imports relatifs implicites de sous-module Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31144: python3 : l'initialisation du type de base 'int' ne prend pas de paramètre Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31145: python3 : retirer les appels à "execfile()", qui n'est plus une fonction native Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31147: python3 : le constructeur de la classe native "object" ne prend pas de paramètre Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31149: python3 : retirer dans le code python et dans les gabarits django les appels à dict.iteritems Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31150: python3 : string.letters n'existe plus Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31151: python3 : déprécier le type natif 'unicode' de python2 Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31152: python3 : utiliser six.StringIO Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31153: python3 : retirer l'appel explicite au module __builtin__ Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31155: python3 : déprécier django.utils.encoding.smart_unicode Solution proposée 06 Mar 2019
Related to Authentic 2 - Development #31156: python3 : utiliser six.moves.http_client à la place de httplib Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31158: python3 : retirer l'usage de tabulations dans les fichiers source python Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31159: python3: déprécier le type natif 'basestring' Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31160: python3 : restreindre à une version antérieure à 4.2 le module pytest utilisé par tox Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31161: python3: fournir un script getlasso3.sh Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31162: python3: prendre en compte un changement d'interface (rétrocompatible) du module uuid Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31163: python3 : utiliser l'encodage hexadécimal de binascii Solution déployée 06 Mar 2019
Related to Authentic 2 - Bug #31164: python3: utiliser six.moves._thread à la place de thread Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31165: python3: utiliser six.moves.reduce à la place de la fonction native reduce Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31166: python3: s'assurer que authentic2.exponential_retry_timeout.ExponentialRetryTimeout.seconds_to_wait retourne un entier Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31167: python3 : corriger l'encodage utf-8 des crédentiels de l'usager dans le fichier de tests principal Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31168: python3 : s'assurer de l'encodage et l'échappement identiques de deux URLs comparées dans tests Solution déployée 06 Mar 2019
Related to Authentic 2 - Bug #31169: python3 : ne plus comparer des chaînes de caractères avec DjangoWebtestResponse.content dans les tests Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31170: python3 : les fonctions de génération du sub OIDC doivent renvoyer du texte Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31171: python3 : gérer les variations d'encodage pour le sous-module authentic2.crypto Solution proposée 06 Mar 2019
Related to Authentic 2 - Development #31172: python3 : gérer l'encodage de la clé secrête OIDC avant chiffrement AES Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31173: python3 : diverses variations d'encodage à gérer dans le fichier de test principal En cours 06 Mar 2019
Related to Authentic 2 - Development #31174: python3 : gérer l'encodage des données passant par le mixin de cache pickle Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31175: python3 : gérer l'encodage base64 des entêtes d'autorisation HTTP Basic dans les tests Solution proposée 06 Mar 2019
Related to Authentic 2 - Development #31176: python3 : utiliser un b'' pour une comparaison dans le fichier de test des APIs Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31178: python3 : déprecier l'usage de str dans le champ SAML PickledObject Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31179: python3 : PIL.Image.open ne prend qu'un chemin de fichier en paramètre Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31180: python3 : choisir la bonne exception à détecter dans le test authn OIDC de décodage en base64 de URL Solution proposée 06 Mar 2019
Related to Authentic 2 - Development #31182: python3 : déprécier l'attribut __metaclass__ Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31183: python3 : encoder l'identifiant d'un fournisseur SAML afin de calculer son haché SHA1 Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31184: python3 : uniformiser le comportement de la méthode magique __str__ des OU Solution déployée 06 Mar 2019
Related to Authentic 2 - Development #31185: python3 : gérer l'encodage ascii des dumps json lors des tests d'import de site Solution déployée 06 Mar 2019

History

#1 Updated by Paul Marillonnet 8 months ago

  • Related to Development #28277: Fonctionner en Python3 pour Django1.11 : fournir un jeu de tests unitaires added

#2 Updated by Paul Marillonnet 8 months ago

#3 Updated by Paul Marillonnet 8 months ago

  • Status changed from Nouveau to Information nécessaire

Benjamin, à l'occasion du sprint GI, tu as évoqué quelques chantiers structurels pour A2 (l'abandon des classes Plugin, notamment, si mes souvenirs sont bons).
Est-ce que tu penses que c'est bloquant pour avancer ici ? Ou bien est-ce qu'on peut faire les deux en parallèle ?

#4 Updated by Benjamin Dauvergne 8 months ago

J'aimerai surtout un unique ticket python3 avec une branche qui avance en parallèle de tout le reste; aussi, mais c'est vrai pour tous les projets, faire un peu attention au code nouveau qu'on commit quand c'est obviously pas compatible, genre préférér django.utils.encoding.force_text(x) plutôt que unicode(x).

#5 Updated by Paul Marillonnet 6 months ago

  • Status changed from Information nécessaire to En cours

Je suis sur une erreur un peu cryptique : http://perso.entrouvert.org/~pmarillonnet/py3-coverage-dj111-authentic-sqlite.html.

Je creuse l'affaire, histoire de voir s'il manque quelque part un marqueur pytest d'accès à la DB django.

#6 Updated by Emmanuel Cazenave 6 months ago

Benjamin Dauvergne a écrit :

J'aimerai surtout un unique ticket python3 avec une branche qui avance en parallèle de tout le reste;

J'avais conseillé à Paul l'inverse il y quelques jours : des petits tickets ciblés qu'on peut pousser petit à petit, c'est comme ça que je m'en suis sortis pour django 1.11.
Au delà des aspects techniques, il y a le problème moral : la longue branche à rebaser en permanence et dont on ne voit jamais le bout, c'est (pour moi) la déprime et l'épuisement assuré.

#7 Updated by Paul Marillonnet 6 months ago

Paul Marillonnet a écrit :

Je suis sur une erreur un peu cryptique : http://perso.entrouvert.org/~pmarillonnet/py3-coverage-dj111-authentic-sqlite.html.

L'erreur n'apparaît plus lorsqu'on utilise une version de pytest inférieure à la 4.2. Merci Benj.

#8 Updated by Benjamin Dauvergne 6 months ago

Finalement c'était une incompatibilité entre mon usage de override_settings sur ROOT_URLCONF et pytest 4.2.0, on peut utiliser pytest 4.2.0 maintenant avec un master à jour.

#9 Updated by Paul Marillonnet 6 months ago

Bon, arrachage de cheveux : le middleware de cache de session est cassé. (La sauvegarde de session provoque une tentative de sérialisation JSON du dictionnaire de session contenant des valeurs de type byte. Ça explose.)
Il faut, je crois, ré-écrire un middleware de session dont la méthode process_response veillerait à transformer les objets bytes en valeur JSON-sérialisable avant sauvegarde.
Ou bien, pour ce faire, hériter directement de django.contrib.sessions.backends.db.SessionStore pour redéfinir les méthodes encode et decode de la classe parente SessionBase1.

Selon la loi du 80-20, ça fait partie des 20% les plus difficiles du travail sur ce ticket, je le ferai à la fin. J'ai commenté le test_session_cache en attendant.

1 Le code des deux méthodes est là, https://docs.djangoproject.com/en/1.11/_modules/django/contrib/sessions/backends/base/. C'est le serialized = self.serializer().dumps(session_dict) dans la méthode encode qui fait tout exploser.

#10 Updated by Benjamin Dauvergne 6 months ago

Paul Marillonnet a écrit :

Bon, arrachage de cheveux : le middleware de cache de session est cassé. (La sauvegarde de session provoque une tentative de sérialisation JSON du dictionnaire de session contenant des valeurs de type byte. Ça explose.)
Il faut, je crois, ré-écrire un middleware de session dont la méthode process_response veillerait à transformer les objets bytes en valeur JSON-sérialisable avant sauvegarde.
Ou bien, pour ce faire, hériter directement de django.contrib.sessions.backends.db.SessionStore pour redéfinir les méthodes encode et decode de la classe parente SessionBase1.

Selon la loi du 80-20, ça fait partie des 20% les plus difficiles du travail sur ce ticket, je le ferai à la fin. J'ai commenté le test_session_cache en attendant.

1 Le code des deux méthodes est là, https://docs.djangoproject.com/en/1.11/_modules/django/contrib/sessions/backends/base/. C'est le serialized = self.serializer().dumps(session_dict) dans la méthode encode qui fait tout exploser.

Il faut trouver d'où viennent ces valeurs bytes et faire en sorte que ce soit de l'unicode, je pense que ça vient du backend LDAP, tu as le nom de la clé ?

#11 Updated by Paul Marillonnet 6 months ago

Le contenu du dictionnaire de session et la trace jusqu'à la sérialisation ici :
http://perso.entrouvert.org/~pmarillonnet/28726_json_encode_error.html

#12 Updated by Benjamin Dauvergne 6 months ago

Ok essaye ça

diff --git a/src/authentic2/decorators.py b/src/authentic2/decorators.py
index 675226d4..83a6a7f1 100644
--- a/src/authentic2/decorators.py
+++ b/src/authentic2/decorators.py
@@ -1,3 +1,4 @@
+import base64
 import pickle
 import re
 from json import dumps as json_dumps
@@ -224,12 +225,17 @@ class DjangoCache(SimpleDictionnaryCacheMixin, CacheDecoratorBase):
 class PickleCacheMixin(object):
     def set(self, key, value):
         value, tstamp = value
-        value = pickle.dumps(value)
+        value = base64.b64encode(pickle.dumps(value))
         super(PickleCacheMixin, self).set(key, (value, tstamp))

     def get(self, key):
         value = super(PickleCacheMixin, self).get(key)
         if value[0] is not None:
+            value, tsamp = value
+            try:
+                value = base64.b64decode(value)
+            except ValueError:
+                pass
             value = (pickle.loads(value[0]), value[1])
         return value

Ça devrait être backward compatible.

#13 Updated by Paul Marillonnet 5 months ago

Non, toujours pas.
django.contrib.sessions.backends.db.SessionStore().encode appelle directement la méthode django.core.signing.JSONSerializer().dumps (qui fait un simple dumps JSON).

En revanche ce sérialisateur est retrouvé via settings.SESSION_SERIALIZER1.
Je propose de redéfinir ce dernier, en patchant les fonctions loads et dumps en reprenant ton patch dans ta dernière remarque, Benjamin.

1 https://github.com/django/django/blob/stable/1.11.x/django/contrib/sessions/backends/base.py#L51

#14 Updated by Paul Marillonnet 5 months ago

Paul Marillonnet a écrit :

Non, toujours pas.

Pardon, en fait si, ça corrige le bogue de sérialisation. Je m'étais pris les pieds dans le tapis.

Benj, j'ai poussé ton patch (avec quelques minimes corrections) dans la branche wip relative au ticket.

#15 Updated by Paul Marillonnet 5 months ago

Avis au(x) relecteur(s) :
Est-ce que je continue à tout pousser dans ma branche à part ?
Ou bien je commence à créer des sous-tickets thématiques, comme l'avait fait Emmanuel pour le support de Django 1.11 ?
(J'en suis à une soixantaine de commits. Environ les deux tiers des tests unitaires passent.)

#16 Updated by Paul Marillonnet 5 months ago

Je me creuse la tête sur le backend LDAP, sans avoir de résultat satisfaisant pour l'instant.
Je dois passer à d'autres dévs, je reviens ici plus tard.

#17 Updated by Benjamin Dauvergne 5 months ago

Avec l'intégration de #30577 ça devrait aller mieux.

#18 Updated by Paul Marillonnet 5 months ago

Oui, je vais essayer.
Je voudrais d'abord créer des petits tickets pour inclure les commits actuels de cette branche wip dans la branche master, sinon je ne vais pas m'en sortir.
(Les changements dans la branche WIP sont trop nombreux, impossible de rebaser sur master, qui vit sa vie aussi de son côté.)

#19 Updated by Paul Marillonnet 5 months ago

#20 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31139: python3: utiliser les fonctions de parsing d'url de django.utils.six added

#21 Updated by Paul Marillonnet 5 months ago

#22 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31143: python3 : corriger les imports relatifs implicites de sous-module added

#23 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31144: python3 : l'initialisation du type de base 'int' ne prend pas de paramètre added

#24 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31145: python3 : retirer les appels à "execfile()", qui n'est plus une fonction native added

#25 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31147: python3 : le constructeur de la classe native "object" ne prend pas de paramètre added

#26 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31149: python3 : retirer dans le code python et dans les gabarits django les appels à dict.iteritems added

#27 Updated by Paul Marillonnet 5 months ago

#28 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31151: python3 : déprécier le type natif 'unicode' de python2 added

#29 Updated by Paul Marillonnet 5 months ago

#30 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31153: python3 : retirer l'appel explicite au module __builtin__ added

#31 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31155: python3 : déprécier django.utils.encoding.smart_unicode added

#32 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31156: python3 : utiliser six.moves.http_client à la place de httplib added

#33 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31158: python3 : retirer l'usage de tabulations dans les fichiers source python added

#34 Updated by Paul Marillonnet 5 months ago

#35 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31160: python3 : restreindre à une version antérieure à 4.2 le module pytest utilisé par tox added

#36 Updated by Paul Marillonnet 5 months ago

#37 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31162: python3: prendre en compte un changement d'interface (rétrocompatible) du module uuid added

#38 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31163: python3 : utiliser l'encodage hexadécimal de binascii added

#39 Updated by Paul Marillonnet 5 months ago

  • Related to Bug #31164: python3: utiliser six.moves._thread à la place de thread added

#40 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31165: python3: utiliser six.moves.reduce à la place de la fonction native reduce added

#41 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31166: python3: s'assurer que authentic2.exponential_retry_timeout.ExponentialRetryTimeout.seconds_to_wait retourne un entier added

#42 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31167: python3 : corriger l'encodage utf-8 des crédentiels de l'usager dans le fichier de tests principal added

#43 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31168: python3 : s'assurer de l'encodage et l'échappement identiques de deux URLs comparées dans tests added

#44 Updated by Paul Marillonnet 5 months ago

  • Related to Bug #31169: python3 : ne plus comparer des chaînes de caractères avec DjangoWebtestResponse.content dans les tests added

#45 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31170: python3 : les fonctions de génération du sub OIDC doivent renvoyer du texte added

#46 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31171: python3 : gérer les variations d'encodage pour le sous-module authentic2.crypto added

#47 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31172: python3 : gérer l'encodage de la clé secrête OIDC avant chiffrement AES added

#48 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31173: python3 : diverses variations d'encodage à gérer dans le fichier de test principal added

#49 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31174: python3 : gérer l'encodage des données passant par le mixin de cache pickle added

#50 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31175: python3 : gérer l'encodage base64 des entêtes d'autorisation HTTP Basic dans les tests added

#51 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31176: python3 : utiliser un b'' pour une comparaison dans le fichier de test des APIs added

#52 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31178: python3 : déprecier l'usage de str dans le champ SAML PickledObject added

#53 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31179: python3 : PIL.Image.open ne prend qu'un chemin de fichier en paramètre added

#54 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31180: python3 : choisir la bonne exception à détecter dans le test authn OIDC de décodage en base64 de URL added

#57 Updated by Paul Marillonnet 5 months ago

#58 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31183: python3 : encoder l'identifiant d'un fournisseur SAML afin de calculer son haché SHA1 added

#59 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31184: python3 : uniformiser le comportement de la méthode magique __str__ des OU added

#60 Updated by Paul Marillonnet 5 months ago

  • Related to Development #31185: python3 : gérer l'encodage ascii des dumps json lors des tests d'import de site added

#61 Updated by Paul Marillonnet 4 months ago

Et donc on a un méta-problème :)
L'enregistrement d'un champ authentic2.compat.JSONField provoque un ajout de champ du champ via la métaclasse (django.db.models.options.Options.add_field).
Cette métaclasse maintient une liste des champs locaux, et l'ajout provoque une insertion dans la liste après détermination de l'emplacement via un bisect.

Le bisect provoque une comparaison entre des objets de classes pour lesquelles les méthodes magiques de comparaison ne sont pas implémentées.

En Python 2, ce n'est pas un souci :

In [2]: class A(object):
   ...:     pass
   ...: 

In [3]: class B(object):
   ...:     pass
   ...: 

In [4]: a = A()

In [5]: b = B()

In [6]: a < b
Out[6]: True

Par contre en Python 3, ce n'est pas la même histoire (et c'est mieux comme ça) :

In [1]: class A(object):
   ...:     pass
   ...: 

In [2]: class B(object):
   ...:     pass
   ...: 

In [3]: a = A()

In [4]: b = B()

In [5]: a < b
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-8c747544e8c8> in <module>()
----> 1 a < b

TypeError: '<' not supported between instances of 'A' and 'B'

Il faut revoir le code de authentic2.compat.JSONField pour prendre en compte, en plus de la compatibilité dj1.8/1.11, ces variations entre les deux versions de Python.

Je ne sais pas encore comment faire ça proprement, ou disons le moins salement possible, je continue de me creuser la tête.

#62 Updated by Frédéric Péters 4 months ago

Je ne sais pas encore comment faire ça proprement, ou disons le moins salement possible, je continue de me creuser la tête.

Faut pas se prendre la tête ici; faut laisser ce bout de côté, passer en Django 1.11 exclusivement, puis revenir sur le sujet.

#63 Updated by Paul Marillonnet 4 months ago

Oui, c'est vrai. Je fais comme ça, merci.

Also available in: Atom PDF