def sql_debug_last_call(response): request = get_current_request() if get_sql_debug() == SQL_DEBUG_LEVEL_WORST and hasattr( request, 'iommi_sql_debug_log' ): # hasattr check because process_request might not be called in case of an early redirect stacks = defaultdict(list) fill_stacks(request.iommi_sql_debug_log) for x in request.iommi_sql_debug_log: stacks[x['sql']].append(x) highscore = sorted([(len(logs), stack_trace, logs) for stack_trace, logs in stacks.items()]) # Print the worst offenders number_of_offenders = getattr(settings, 'SQL_DEBUG_WORST_NUMBER_OF_OFFENDERS', 3) query_cutoff = getattr(settings, 'SQL_DEBUG_WORST_QUERY_CUTOFF', 4) num_suspicious = getattr(settings, 'SQL_DEBUG_WORST_SUSPICIOUS_CUTOFF', 3) for count, _, logs in highscore[-number_of_offenders:]: if count > num_suspicious: # 3 times is ok-ish, more is suspicious sql_debug(f'------ {count} times: -------', bold=True) if hasattr(response, 'iommi_part'): from iommi.debug import filename_and_line_num_from_part filename, lineno = filename_and_line_num_from_part( response.iommi_part) sql_debug('From source:') sql_debug(format_clickable_filename( filename, lineno, str(response.iommi_part._name)), sql_trace=True) sql_debug('With Stack:') sql_debug(logs[0]['stack'], sql_trace=True) for x in logs[:query_cutoff]: sql_debug_trace_sql(**x) if len(logs) > query_cutoff: sql_debug( f'... and {len(logs) - query_cutoff:d} more unique statements\n' ) queries_per_using = defaultdict(int) for x in request.iommi_sql_debug_log: queries_per_using[x['using']] += 1 sql_debug( f'Total number of SQL statements: {sum(queries_per_using.values())}\n' ) if settings.DEBUG: total_sql_time = f" (sql time: {sql_debug_total_time():.3f}s)" duration = f' ({(datetime.now() - request.iommi_start_time).total_seconds():.3f}s)' sql_debug( msg= f'{request.META["REQUEST_METHOD"]} {request.get_full_path()} -> {response.status_code} {duration}{total_sql_time}' ) sql_debug( f'{request.get_full_path()} -> {response.status_code}{duration}', fg='magenta') set_sql_debug(None)
def live_edit_view(request, view, args, kwargs): view = get_wrapped_view(view) # Read the old code try: # view is a function based view filename = view.__globals__['__file__'] except AttributeError: # view is an iommi class from iommi.debug import filename_and_line_num_from_part filename, _ = filename_and_line_num_from_part(view) with open(filename) as f: entire_file = f.read() ast_of_entire_file = parso.parse(entire_file) is_unix_line_endings = '\r\n' not in entire_file ast_of_old_code = find_view(view, ast_of_entire_file) assert ast_of_old_code is not None flow_direction = request.GET.get('_iommi_live_edit') or 'column' assert flow_direction in ('column', 'row') if request.method == 'POST': try: code = request.POST['data'].replace('\t', ' ') if is_unix_line_endings: code = code.replace('\r\n', '\n') final_result = dangerous_execute_code(code, request, view, args, kwargs) if orig_reload is not None: # A little monkey patch dance to avoid one reload of the runserver when it's just us writing the code to disk # This only works in django 2.2+ def restore_auto_reload(filename): from django.utils import autoreload print('Skipped reload') autoreload.trigger_reload = orig_reload autoreload.trigger_reload = restore_auto_reload if isinstance(view, Part): ast_of_new_code = find_node(name=view.__class__.__name__, node=parso.parse(code), node_type='classdef') else: ast_of_new_code = find_node(name=view.__name__, node=parso.parse(code), node_type='funcdef') ast_of_old_code.children[:] = ast_of_new_code.children new_code = ast_of_entire_file.get_code() with open(filename, 'w') as f: f.write(new_code) return final_result except Exception as e: import traceback traceback.print_exc() error = str(e) if not error: error = str(e.__class__) return HttpResponse(json.dumps(dict(error=error))) return LiveEditPage( title='iommi live edit', h_tag__include=False, assets__code_editor=Asset.js( attrs=dict( src='https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js', integrity= 'sha512-GZ1RIgZaSc8rnco/8CXfRdCpDxRCphenIiZ2ztLy3XQfCbQUSCuk8IudvNHxkRA3oUg6q0qejgN/qqyG1duv5Q==', crossorigin='anonymous', ), after=-1, ), assets__live_edit_page_custom=Asset( tag='style', text=''' .container { padding: 0 !important; margin: 0 !important; max-width: 100%; } html, body { height: 100%; margin: 0; } .container { display: flex; flex-flow: <<flow_direction>>; height: 100%; } .container iframe { flex: 1 1 auto; } .container #editor { flex: 2 1 auto; } '''.replace('<<flow_direction>>', flow_direction), ), parts__result=html.iframe(attrs__id='result'), parts__editor=html.div( ast_of_old_code.get_code(), attrs__id='editor', ), parts__script=html.script( mark_safe(''' function iommi_debounce(func, wait) { let timeout; return (...args) => { const fn = () => func.apply(this, args); clearTimeout(timeout); timeout = setTimeout(() => fn(), wait); }; } var editor = ace.edit("editor"); editor.setTheme("ace/theme/cobalt"); editor.session.setMode("ace/mode/python"); editor.setShowPrintMargin(false); async function update() { let form_data = new FormData(); form_data.append('data', editor.getValue()); let response = await fetch('', { method: 'POST', body: form_data }); let foo = await response.json(); if (foo.page) { // TODO: get scroll position and restore it document.getElementById('result').srcdoc = foo.page; } } function foo() { iommi_debounce(update, 200)(); } editor.session.on('change', foo); editor.setFontSize(14); editor.session.setUseWrapMode(true); foo(); ''')), )
def test_filename_and_line_num_from_part_empty_case(): assert filename_and_line_num_from_part(part=Struct( _instantiated_at_frame=Struct(f_back=None))) == (None, None)