Flask - TypeError: l'objet de type cycle n'est pas sérialisable JSON

Sep 26 2020

J'essaie de renvoyer une réponse json du serveur au client après que le client ait publié un message via AJAX.

Comme ça, ça marche:

@app.route("/test", methods=["GET", "POST"])
@login_required
def test():

  if request.is_xhr:

    try:

      json_response = {
        "result": "success"
      }

    except Exception as e:
      err = _except(line=sys.exc_info()[-1].tb_lineno, error=e, function_name=what_func(), script_name=__file__)
      json_response = {"result": "failure", "msg": err}

    return jsonify(json_response)

  else:
    return redirect("/not-found")

  return ''

Si j'aime ça, ça ne marche pas:

@app.route("/test", methods=["GET", "POST"])
@login_required
def test():

  if request.is_xhr:

    try:

      _dict = {
        "key1": "value1",
        "key2": "value2"
      }

      if not "my_dict" in session:
        session["my_dict"] = [_dict]
      else:
        session["my_dict"] = session["my_dict"] + [_dict]

      session.modified = True

      json_response = {
        "result": "success"
      }

    except Exception as e:
      err = _except(line=sys.exc_info()[-1].tb_lineno, error=e, function_name=what_func(), script_name=__file__)
      json_response = {"result": "failure", "msg": err}

    return jsonify(json_response)

  else:
    return redirect("/not-found")

  return ''

J'obtiens l'erreur suivante et le client ne reçoit pas de réponse json:

File "~/myprojectenv/lib/python3.8/site-packages/flask/json/__init__.py", line 100, in default
    return _json.JSONEncoder.default(self, o)
  File "/usr/lib/python3.8/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type cycle is not JSON serializable
[2020-09-26 19:01:14,091] ERROR in app: Request finalizing failed with an error while handling an error

Si je supprime cet étirement:

      if not "my_dict" in session:
        session["my_dict"] = [_dict]
      else:
        session["my_dict"] = session["my_dict"] + [_dict]

      session.modified = True

Cela fonctionne à nouveau. Le client reçoit la réponse json.

Réponses

rjunior8 Sep 26 2020 at 22:08

J'ai compris le problème.

Découvrez la fonction suivante:

@app.route("/test", methods=["GET", "POST"])
def test():
    if request.is_xhr:
        try:

            licycle = cycle(files)
            nextelem = next(licycle)

            _dict = {
                "sid": session["_id"],
                "licycle": licycle,
                "nextelem": nextelem,
                "licycle2": licycle2,
                "nextelem2": nextelem2,
                "mon_id": _id,
                "sch_id": 0
            }

            if not "all_submons" in session:
                session["all_submons"] = [_dict]
            else:
                session["all_submons"] = session["all_submons"] + [_dict]

            session.modified = True

            all_submons.setdefault("sub_mons", []).append(_dict)

            json_response = {"result": "success"}

        except Exception as e:
            err = _except(line=sys.exc_info()[-1].tb_lineno, error=e, function_name=what_func(), script_name=__file__)
            json_response = {"result": "failure", "err": err}
        finally:
            try:
                print("exiting...")
            except Exception as e:
                pass

        return jsonify(json_response)

    else:
        return redirect("/not-found")

    return ""

La raison en est que le type de variable licycle est <class 'itertools.cycle'> et la session "n'accepte probablement pas" ce type de variable comme un dictionnaire accepte comme vous pouvez le voir dans ma variable dict all_submons.

Le point est:

L'exécution NE TOMBE PAS en exception. Et _dict est stocké sur la session comme vous pouvez le voir ensuite.

print(f"Session before: {session}")
print(f"all_submons before: {all_submons}")

if not "all_submons" in session:
    session["all_submons"] = [_dict]
else:
    session["all_submons"] = session["all_submons"] + [_dict]

session.modified = True

all_submons.setdefault("sub_mons", []).append(_dict)

print(f"Session after: {session}")
print(f"all_submons after: {all_submons}\n")

Vous pouvez vérifier la sortie:

Session before: <SecureCookieSession {'_fresh': True, '_id': 'e253a950...', 'all_submons': [], 'user_id': '1'}>

all_submons before: {}

Session after: <SecureCookieSession {'_fresh': True, '_id': 'e253a...', 'all_submons': [{'sid': 'e253a95...', 'licycle': <itertools.cycle object at 0x7fc989237280>, 'nextelem': ('1a4add0f275c7275.jpg',), 'licycle2': None, 'nextelem2': None, 'mon_id': 1, 'sch_id': 0}], 'user_id': '1'}>

all_submons after: {'sub_mons': [{'sid': 'e253a...', 'licycle': <itertools.cycle object at 0x7fd6f1a17b80>, 'nextelem': ('1a4add0f275c7275.jpg',), 'licycle2': None, 'nextelem2': None, 'mon_id': 1, 'sch_id': 0}]}

Je ne suis pas sûr que la session "n'accepte probablement pas" ce type de variable - <class 'itertools.cycle'>

Mais j'ai créé un autre dictionnaire avec d'autres variables, sans type de itertools.cycle et cela a fonctionné.