Projet

Général

Profil

0005-forms-abort-autosave-after-200-ms-58276.patch

Benjamin Dauvergne, 06 novembre 2021 14:27

Télécharger (3,62 ko)

Voir les différences:

Subject: [PATCH 5/5] forms: abort autosave after 200 ms (#58276)

 tests/form_pages/test_all.py | 24 ++++++++++++++++++++++++
 wcs/forms/root.py            | 12 ++++++++++++
 wcs/qommon/misc.py           | 26 ++++++++++++++++++++++++++
 3 files changed, 62 insertions(+)
tests/form_pages/test_all.py
4276 4276
    assert formdef.data_class().select()[0].data['1'] == 'foobar3'
4277 4277

  
4278 4278

  
4279
def test_form_autosave_timeout(pub, monkeypatch):
4280
    from wcs.forms.root import FormPage
4281

  
4282
    monkeypatch.setattr(FormPage, 'AUTOSAVE_TIMEOUT', 0.0001)
4283

  
4284
    formdef = create_formdef()
4285
    formdef.fields = [
4286
        fields.PageField(id='0', label='1st page', type='page'),
4287
        fields.StringField(id='1', label='string'),
4288
        fields.PageField(id='2', label='2nd page', type='page'),
4289
        fields.StringField(id='3', label='string 2'),
4290
    ]
4291
    formdef.enable_tracking_codes = True
4292
    formdef.store()
4293

  
4294
    formdef.data_class().wipe()
4295
    app = get_app(pub)
4296
    resp = app.get('/test/')
4297
    resp.form['f1'] = 'foobar'
4298

  
4299
    resp = app.post('/test/autosave', params=resp.form.submit_fields())
4300
    assert resp.json == {'reason': 'autosave took more than 0.0001 seconds', 'result': 'error'}
4301

  
4302

  
4279 4303
def test_form_autosave_with_items_field(pub):
4280 4304
    formdef = create_formdef()
4281 4305
    formdef.data_class().wipe()
wcs/forms/root.py
1297 1297
            formdata.user = get_request().user
1298 1298
        formdata.store()
1299 1299

  
1300
    AUTOSAVE_TIMEOUT = 0.2
1301

  
1300 1302
    def autosave(self):
1303
        try:
1304
            with misc.timeout(self.AUTOSAVE_TIMEOUT):
1305
                return self._autosave()
1306
        except misc.TimeoutException:
1307
            get_request().ignore_session = True
1308
            return json.dumps(
1309
                {'result': 'error', 'reason': 'autosave took more than %s seconds' % self.AUTOSAVE_TIMEOUT}
1310
            )
1311

  
1312
    def _autosave(self):
1301 1313
        get_response().set_content_type('application/json')
1302 1314

  
1303 1315
        def result_error(reason):
wcs/qommon/misc.py
16 16

  
17 17
import base64
18 18
import calendar
19
import contextlib
19 20
import datetime
20 21
import decimal
21 22
import hashlib
......
24 25
import json
25 26
import os
26 27
import re
28
import signal
27 29
import subprocess
28 30
import time
29 31
import unicodedata
......
1089 1091
    }
1090 1092
    object_type_name = object_type_names.get(value.__class__, value.__class__.__name__)
1091 1093
    return object_type_name
1094

  
1095

  
1096
class TimeoutException(Exception):
1097
    pass
1098

  
1099

  
1100
def _alarm_handler_for_timeout(signum, frame):
1101
    raise TimeoutException
1102

  
1103

  
1104
# this context manager cannot be nested, only one timeout can be set at a time,
1105
# enclosing timer will be reset
1106
@contextlib.contextmanager
1107
def timeout(duration):
1108
    old_value = signal.getsignal(signal.SIGALRM)
1109
    try:
1110
        signal.signal(signal.SIGALRM, _alarm_handler_for_timeout)
1111
        try:
1112
            signal.setitimer(signal.ITIMER_REAL, duration)
1113
            yield
1114
        finally:
1115
            signal.setitimer(signal.ITIMER_REAL, 0)
1116
    finally:
1117
        signal.signal(signal.SIGALRM, old_value)
1092
-