NaserNajeh commited on
Commit
7e69d18
·
verified ·
1 Parent(s): 3df1c8f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +368 -99
app.py CHANGED
@@ -1,146 +1,415 @@
1
- import os, io, traceback
 
 
 
 
2
  import gradio as gr
3
  import fitz # PyMuPDF
4
  from PIL import Image
5
- import spaces # ل ZeroGPU
6
 
7
- # نعطّل مسار الفيديو داخل Transformers حتى لا يحمّل AutoVideoProcessor (ويطلب torchvision)
 
 
 
 
8
  os.environ["TRANSFORMERS_NO_TORCHVISION"] = "1"
9
 
10
- # أسماء النماذج (يمكن تعديلها من Settings → Variables)
11
  BASE_MODEL = os.environ.get("BASE_MODEL", "Qwen/Qwen2-VL-2B-Instruct")
12
  HOROOF_ADAPTER = os.environ.get("HOROOF_MODEL", "NaserNajeh/Horoof")
13
 
 
14
  _model = None
15
  _tokenizer = None
16
  _img_proc = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  def load_model_merged():
19
  """
20
- - تحميل Qwen2-VL-2B (base) على GPU (fp16)
21
- - تركيب LoRA Horoof ودمجها (merge_and_unload) → نموذج نهائي بدون bitsandbytes
22
- - استخدام Qwen2VLImageProcessor (صور فقط) + AutoTokenizer، بدون أي video/torchvision
23
  """
24
- global _model, _tokenizer, _img_proc
25
- if _model is not None:
 
 
26
  return
 
27
  try:
28
- import torch
 
 
 
29
  from transformers import (
30
  Qwen2VLForConditionalGeneration,
31
  AutoTokenizer,
32
  Qwen2VLImageProcessor
33
  )
34
  from peft import PeftModel
35
-
36
- if not torch.cuda.is_available():
37
- raise AssertionError("هذه النسخة تتطلب GPU (CUDA) مفعّل على الـSpace.")
38
-
39
- # Tokenizer + ImageProcessor (بدون VideoProcessor)
40
- _tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL, trust_remote_code=False)
41
- _img_proc = Qwen2VLImageProcessor.from_pretrained(BASE_MODEL, trust_remote_code=False)
42
-
43
- # Base model
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  base = Qwen2VLForConditionalGeneration.from_pretrained(
45
- BASE_MODEL, torch_dtype=torch.float16
46
- ).to("cuda")
47
-
48
- # Merge LoRA
 
 
 
 
 
 
49
  peft_model = PeftModel.from_pretrained(base, HOROOF_ADAPTER)
50
- _model = peft_model.merge_and_unload().to("cuda")
51
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  except Exception as e:
 
 
53
  raise RuntimeError(f"تعذّر تحميل النموذج: {e}")
54
 
55
  def pdf_to_images(pdf_bytes: bytes, dpi: int = 220, max_pages: int = 0):
56
- """تحويل PDF إلى صور PIL."""
57
- pages_imgs = []
58
- doc = fitz.open(stream=pdf_bytes, filetype="pdf")
59
- total = doc.page_count
60
- n_pages = total if (not max_pages or max_pages <= 0) else min(max_pages, total)
61
- for i in range(n_pages):
62
- page = doc.load_page(i)
63
- pix = page.get_pixmap(dpi=dpi, alpha=False)
64
- img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
65
- pages_imgs.append((i + 1, img))
66
- doc.close()
67
- return pages_imgs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
  def ocr_page_gpu(pil_img: Image.Image, max_new_tokens: int = 1200) -> str:
70
  """
71
- OCR لصفحة واحدة باستخدام Qwen2-VL + LoRA (Horoof) — عبر
72
- AutoTokenizer + Qwen2VLImageProcessor فقط (بدون torchvision).
73
  """
74
- load_model_merged()
75
- import torch
76
-
77
- # رسالة محادثة بصيغة Qwen2-VL (صورة + نص)
78
- messages = [
79
- {
80
- "role": "user",
81
- "content": [
82
- {"type": "image", "image": pil_img},
83
- {"type": "text", "text": "اقرأ النص العربي في الصورة كما هو دون أي تعديل أو تفسير."},
84
- ],
85
- }
86
- ]
87
-
88
- # 1) نحصل على input_ids مباشرة (tokenize=True) — بدون الحاجة لـ Processor موحّد
89
- tok = _tokenizer.apply_chat_template(
90
- messages,
91
- add_generation_prompt=True,
92
- tokenize=True,
93
- return_tensors="pt"
94
- )
95
 
96
- # 2) تجهيز الصورة (صور فقط → لا فيديو) عبر Qwen2VLImageProcessor
97
- vis = _img_proc(images=[pil_img], return_tensors="pt")
 
 
 
 
 
 
 
 
98
 
99
- # 3) تجميع المدخلات وإرسالها إلى الـGPU
100
- inputs = {"input_ids": tok}
101
- inputs.update(vis) # يضيف pixel_values و image_grid_thw و image_sizes (إن لزم)
102
- for k, v in list(inputs.items()):
103
- if hasattr(v, "to"):
104
- inputs[k] = v.to("cuda")
 
 
105
 
106
- # 4) التوليد
107
- with torch.inference_mode():
108
- output_ids = _model.generate(**inputs, max_new_tokens=max_new_tokens)
109
- out = _tokenizer.batch_decode(output_ids, skip_special_tokens=True)[0]
110
- return (out or "").strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
- @spaces.GPU # ضروري ل ZeroGPU
113
  def ocr_pdf(pdf_file, dpi, limit_pages):
114
- """الدالة الرئيسة التي يستدعيها Gradio."""
 
115
  if pdf_file is None:
116
- return "لم يتم رفع ملف."
 
117
  try:
118
- pdf_bytes = pdf_file.read() if hasattr(pdf_file, "read") else pdf_file
119
- limit = int(limit_pages) if limit_pages else 1 # صفحة واحدة افتراضيًا للاختبار
120
- pages = pdf_to_images(pdf_bytes, dpi=int(dpi), max_pages=limit)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  if not pages:
122
- return "لا توجد صفحات."
123
- outs = []
124
- for idx, img in pages:
125
- txt = ocr_page_gpu(img)
126
- outs.append(f"--- صفحة {idx} ---\n{txt}")
127
- return "\n\n".join(outs)
128
- except AssertionError as ae:
129
- return f"⚠️ {ae}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  except Exception as e:
131
- traceback.print_exc()
132
- return f"حدث خطأ: {repr(e)}"
 
133
 
134
- with gr.Blocks(title="Horoof OCR (ZeroGPU)") as demo:
135
- gr.Markdown("### Horoof OCR على ZeroGPU — Qwen2-VL + LoRA (مُدمج)، بدون torchvision/bitsandbytes.")
136
- pdf_in = gr.File(label="ارفع ملف PDF", file_types=[".pdf"], type="binary")
137
- dpi = gr.Slider(150, 300, value=220, step=10, label="دقّة التحويل (DPI)")
138
- limit_pages = gr.Number(value=1, precision=0, label="عدد الصفحات (اختبار؛ زِد لاحقًا)")
139
- run_btn = gr.Button("بدء التحويل")
140
- out = gr.Textbox(label="النص المستخرج", lines=24)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
- demo.queue()
143
- run_btn.click(fn=ocr_pdf, inputs=[pdf_in, dpi, limit_pages], outputs=out, api_name="ocr_pdf")
 
 
 
144
 
145
  if __name__ == "__main__":
146
- demo.launch()
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import traceback
4
+ import logging
5
+ import torch
6
  import gradio as gr
7
  import fitz # PyMuPDF
8
  from PIL import Image
9
+ import spaces # للـ ZeroGPU
10
 
11
+ # إعداد نظام التسجيل
12
+ logging.basicConfig(level=logging.INFO)
13
+ logger = logging.getLogger(__name__)
14
+
15
+ # تعطيل مسار الفيديو داخل Transformers
16
  os.environ["TRANSFORMERS_NO_TORCHVISION"] = "1"
17
 
18
+ # أسماء النماذج
19
  BASE_MODEL = os.environ.get("BASE_MODEL", "Qwen/Qwen2-VL-2B-Instruct")
20
  HOROOF_ADAPTER = os.environ.get("HOROOF_MODEL", "NaserNajeh/Horoof")
21
 
22
+ # متغيرات النموذج العامة
23
  _model = None
24
  _tokenizer = None
25
  _img_proc = None
26
+ _model_loaded = False
27
+
28
+ def check_gpu_availability():
29
+ """التحقق من توفر GPU وطباعة معلومات النظام"""
30
+ if not torch.cuda.is_available():
31
+ raise AssertionError("هذه النسخة تتطلب GPU (CUDA) مفعّل على الـSpace.")
32
+
33
+ device_name = torch.cuda.get_device_name(0)
34
+ memory_gb = torch.cuda.get_device_properties(0).total_memory / 1024**3
35
+
36
+ logger.info(f"GPU متاح: {device_name}")
37
+ logger.info(f"ذاكرة GPU: {memory_gb:.1f} GB")
38
+
39
+ return device_name, memory_gb
40
+
41
+ def clear_gpu_cache():
42
+ """تنظيف ذاكرة GPU"""
43
+ if torch.cuda.is_available():
44
+ torch.cuda.empty_cache()
45
+ torch.cuda.synchronize()
46
 
47
  def load_model_merged():
48
  """
49
+ تحميل النموذج مع تحسينات إضافية وإدارة أفضل للذاكرة
 
 
50
  """
51
+ global _model, _tokenizer, _img_proc, _model_loaded
52
+
53
+ if _model_loaded and _model is not None:
54
+ logger.info("النموذج محمّل بالفعل")
55
  return
56
+
57
  try:
58
+ # التحقق من GPU
59
+ device_name, memory_gb = check_gpu_availability()
60
+
61
+ # استيراد المكتبات
62
  from transformers import (
63
  Qwen2VLForConditionalGeneration,
64
  AutoTokenizer,
65
  Qwen2VLImageProcessor
66
  )
67
  from peft import PeftModel
68
+
69
+ logger.info("بدء تحميل النموذج...")
70
+
71
+ # تحميل Tokenizer و ImageProcessor
72
+ logger.info("تحميل المعالجات...")
73
+ _tokenizer = AutoTokenizer.from_pretrained(
74
+ BASE_MODEL,
75
+ trust_remote_code=False,
76
+ use_fast=True # تسريع التوكين
77
+ )
78
+ _img_proc = Qwen2VLImageProcessor.from_pretrained(
79
+ BASE_MODEL,
80
+ trust_remote_code=False
81
+ )
82
+
83
+ # تحميل النموذج الأساسي مع تحسينات الذاكرة
84
+ logger.info(f"تحميل النموذج الأساسي: {BASE_MODEL}")
85
+
86
+ # تحسين تحميل النموذج حسب حجم الذاكرة
87
+ if memory_gb >= 40: # H100 أو A100
88
+ model_kwargs = {
89
+ "torch_dtype": torch.float16,
90
+ "device_map": "auto",
91
+ "low_cpu_mem_usage": True
92
+ }
93
+ else: # GPUs أصغر
94
+ model_kwargs = {
95
+ "torch_dtype": torch.float16,
96
+ "low_cpu_mem_usage": True
97
+ }
98
+
99
  base = Qwen2VLForConditionalGeneration.from_pretrained(
100
+ BASE_MODEL,
101
+ **model_kwargs
102
+ )
103
+
104
+ # نقل إلى GPU إذا لم يتم تلقائياً
105
+ if not hasattr(base, 'device') or str(base.device) == 'cpu':
106
+ base = base.to("cuda")
107
+
108
+ # تحميل ودمج LoRA
109
+ logger.info(f"تحميل LoRA adapter: {HOROOF_ADAPTER}")
110
  peft_model = PeftModel.from_pretrained(base, HOROOF_ADAPTER)
111
+
112
+ logger.info("دمج LoRA مع النموذج الأساسي...")
113
+ _model = peft_model.merge_and_unload()
114
+
115
+ # التأكد من وجود النموذج على GPU
116
+ if not str(_model.device).startswith('cuda'):
117
+ _model = _model.to("cuda")
118
+
119
+ # تحسين للاستنتاج
120
+ _model.eval()
121
+
122
+ # تمكين optimizations
123
+ if hasattr(_model, 'half'):
124
+ _model = _model.half()
125
+
126
+ _model_loaded = True
127
+ logger.info("تم تحميل النموذج بنجاح!")
128
+
129
+ # طباعة معلومات الذاكرة
130
+ if torch.cuda.is_available():
131
+ allocated = torch.cuda.memory_allocated() / 1024**3
132
+ logger.info(f"ذاكرة GPU المستخدمة: {allocated:.2f} GB")
133
+
134
  except Exception as e:
135
+ logger.error(f"خطأ في تحميل النموذج: {str(e)}")
136
+ logger.error(traceback.format_exc())
137
  raise RuntimeError(f"تعذّر تحميل النموذج: {e}")
138
 
139
  def pdf_to_images(pdf_bytes: bytes, dpi: int = 220, max_pages: int = 0):
140
+ """
141
+ تحويل PDF إلى صور PIL مع تحسينات
142
+ """
143
+ try:
144
+ pages_imgs = []
145
+ doc = fitz.open(stream=pdf_bytes, filetype="pdf")
146
+ total = doc.page_count
147
+
148
+ logger.info(f"عدد صفحات PDF: {total}")
149
+
150
+ # تحديد عدد الصفحات للمعالجة
151
+ n_pages = total if (not max_pages or max_pages <= 0) else min(max_pages, total)
152
+
153
+ for i in range(n_pages):
154
+ try:
155
+ page = doc.load_page(i)
156
+
157
+ # تحسين جودة التحويل
158
+ mat = fitz.Matrix(dpi/72, dpi/72)
159
+ pix = page.get_pixmap(matrix=mat, alpha=False)
160
+
161
+ # تحويل لـ PIL Image
162
+ img_data = pix.samples
163
+ img = Image.frombytes("RGB", [pix.width, pix.height], img_data)
164
+
165
+ # تحسين حجم الصورة إذا كانت كبيرة جداً
166
+ max_dimension = 2048
167
+ if max(img.size) > max_dimension:
168
+ img.thumbnail((max_dimension, max_dimension), Image.Resampling.LANCZOS)
169
+ logger.info(f"تم تصغير الصفحة {i+1} إلى {img.size}")
170
+
171
+ pages_imgs.append((i + 1, img))
172
+ logger.info(f"تم تحويل الصفحة {i+1}/{n_pages}")
173
+
174
+ except Exception as e:
175
+ logger.error(f"خطأ في تحويل الصفحة {i+1}: {str(e)}")
176
+ continue
177
+
178
+ doc.close()
179
+ logger.info(f"تم تحويل {len(pages_imgs)} صفحة بنجاح")
180
+ return pages_imgs
181
+
182
+ except Exception as e:
183
+ logger.error(f"خطأ في فتح PDF: {str(e)}")
184
+ raise
185
 
186
  def ocr_page_gpu(pil_img: Image.Image, max_new_tokens: int = 1200) -> str:
187
  """
188
+ OCR لصفحة واحدة مع تحسينات الأداء
 
189
  """
190
+ try:
191
+ # التأكد من تحميل النموذج
192
+ load_model_merged()
193
+
194
+ # تنظيف الذاكرة قبل المعالجة
195
+ clear_gpu_cache()
196
+
197
+ # رسالة المحادثة
198
+ messages = [
199
+ {
200
+ "role": "user",
201
+ "content": [
202
+ {"type": "image", "image": pil_img},
203
+ {"type": "text", "text": "اقرأ النص العربي في الصورة كما هو دون أي تعديل أو تفسير."},
204
+ ],
205
+ }
206
+ ]
 
 
 
 
207
 
208
+ # توكين النص
209
+ tok = _tokenizer.apply_chat_template(
210
+ messages,
211
+ add_generation_prompt=True,
212
+ tokenize=True,
213
+ return_tensors="pt"
214
+ )
215
+
216
+ # معالجة الصورة
217
+ vis = _img_proc(images=[pil_img], return_tensors="pt")
218
 
219
+ # تجهيز المدخلات
220
+ inputs = {"input_ids": tok}
221
+ inputs.update(vis)
222
+
223
+ # نقل إلى GPU
224
+ for k, v in inputs.items():
225
+ if hasattr(v, "to"):
226
+ inputs[k] = v.to("cuda")
227
 
228
+ # التوليد مع تحسينات
229
+ with torch.inference_mode():
230
+ output_ids = _model.generate(
231
+ **inputs,
232
+ max_new_tokens=max_new_tokens,
233
+ do_sample=False, # للحصول على نتائج مستقرة
234
+ temperature=0.1,
235
+ pad_token_id=_tokenizer.eos_token_id,
236
+ use_cache=True
237
+ )
238
+
239
+ # فك التشفير
240
+ generated_part = output_ids[0][len(inputs["input_ids"][0]):]
241
+ result = _tokenizer.decode(generated_part, skip_special_tokens=True)
242
+
243
+ # تنظيف النتيجة
244
+ result = result.strip()
245
+
246
+ # تنظيف الذاكرة بعد المعالجة
247
+ clear_gpu_cache()
248
+
249
+ return result if result else "لم يتم استخراج أي نص"
250
+
251
+ except Exception as e:
252
+ logger.error(f"خطأ في OCR: {str(e)}")
253
+ clear_gpu_cache()
254
+ return f"خطأ في معالجة الصورة: {str(e)}"
255
 
256
+ @spaces.GPU # ضروري لـ ZeroGPU
257
  def ocr_pdf(pdf_file, dpi, limit_pages):
258
+ """الدالة الرئيسية مع تحسينات إضافية"""
259
+
260
  if pdf_file is None:
261
+ return "لم يتم رفع ملف PDF."
262
+
263
  try:
264
+ logger.info("بدء معالجة PDF...")
265
+
266
+ # قراءة البيانات
267
+ if hasattr(pdf_file, 'read'):
268
+ pdf_bytes = pdf_file.read()
269
+ else:
270
+ pdf_bytes = pdf_file
271
+
272
+ if not pdf_bytes:
273
+ return "❌ الملف فارغ أو تالف."
274
+
275
+ # تحويل إلى صور
276
+ limit = max(1, int(limit_pages)) if limit_pages else 1
277
+ dpi_val = max(150, min(300, int(dpi))) # تحديد نطاق DPI
278
+
279
+ logger.info(f"تحويل {limit} صفحة بدقة {dpi_val} DPI...")
280
+ pages = pdf_to_images(pdf_bytes, dpi=dpi_val, max_pages=limit)
281
+
282
  if not pages:
283
+ return "لا توجد صفحات صالحة للمعالجة."
284
+
285
+ # معالجة OCR
286
+ results = []
287
+ total_pages = len(pages)
288
+
289
+ for i, (page_num, img) in enumerate(pages, 1):
290
+ try:
291
+ logger.info(f"معالجة الصفحة {page_num} ({i}/{total_pages})...")
292
+
293
+ # OCR للصفحة
294
+ text = ocr_page_gpu(img)
295
+
296
+ if text and text.strip():
297
+ results.append(f"📄 **صفحة {page_num}**\n{'-'*50}\n{text}")
298
+ else:
299
+ results.append(f"📄 **صفحة {page_num}**\n{'-'*50}\n⚠️ لم يتم استخراج نص من هذه الصفحة")
300
+
301
+ except Exception as e:
302
+ logger.error(f"خطأ في معالجة الصفحة {page_num}: {str(e)}")
303
+ results.append(f"📄 **صفحة {page_num}**\n{'-'*50}\n❌ خطأ في المعالجة: {str(e)}")
304
+
305
+ if not results:
306
+ return "❌ لم يتم استخراج أي نص من الملف."
307
+
308
+ final_result = "\n\n".join(results)
309
+ logger.info(f"تمت معالجة {len(results)} صفحة بنجاح")
310
+
311
+ return final_result
312
+
313
  except Exception as e:
314
+ logger.error(f"خطأ عام في المعالجة: {str(e)}")
315
+ logger.error(traceback.format_exc())
316
+ return f"❌ حدث خطأ: {str(e)}"
317
 
318
+ # إنشاء واجهة Gradio
319
+ with gr.Blocks(
320
+ title="Horoof OCR (H200 GPU)",
321
+ theme=gr.themes.Soft(),
322
+ css="""
323
+ .gradio-container {
324
+ font-family: 'Arial', sans-serif;
325
+ }
326
+ .output-text {
327
+ font-family: 'Courier New', monospace;
328
+ line-height: 1.6;
329
+ }
330
+ """
331
+ ) as demo:
332
+
333
+ gr.Markdown("""
334
+ # 🔤 Horoof OCR - استخراج النصوص العربية
335
+
336
+ **النموذج**: Qwen2-VL-2B + Horoof LoRA
337
+ **المعالج**: H200 GPU
338
+ **الدعم**: PDF → نص عربي عالي الجودة
339
+
340
+ ---
341
+ """)
342
+
343
+ with gr.Row():
344
+ with gr.Column(scale=1):
345
+ pdf_input = gr.File(
346
+ label="📁 ارفع ملف PDF",
347
+ file_types=[".pdf"],
348
+ type="binary"
349
+ )
350
+
351
+ dpi_slider = gr.Slider(
352
+ minimum=150,
353
+ maximum=300,
354
+ value=220,
355
+ step=10,
356
+ label="🎯 دقة التحويل (DPI)",
357
+ info="دقة أعلى = جودة أفضل + وقت أطول"
358
+ )
359
+
360
+ pages_limit = gr.Number(
361
+ value=2,
362
+ minimum=1,
363
+ maximum=10,
364
+ precision=0,
365
+ label="📊 عدد الصفحات المراد معالجتها",
366
+ info="ابدأ بعدد قليل للاختبار"
367
+ )
368
+
369
+ process_btn = gr.Button(
370
+ "🚀 بدء الاستخراج",
371
+ variant="primary",
372
+ size="lg"
373
+ )
374
+
375
+ with gr.Column(scale=2):
376
+ output_text = gr.Textbox(
377
+ label="📝 النص المستخرج",
378
+ lines=25,
379
+ max_lines=30,
380
+ elem_classes=["output-text"],
381
+ placeholder="سيظهر النص المستخرج هنا...",
382
+ show_copy_button=True
383
+ )
384
+
385
+ gr.Markdown("""
386
+ ---
387
+ ### 💡 نصائح للحصول على أفضل النتائج:
388
+ - **جودة الملف**: تأكد من أن PDF واضح وقابل للقراءة
389
+ - **DPI**: استخدم 220-250 للنصوص العادية، 280-300 للخطوط الصغيرة
390
+ - **عدد الصفحات**: ابدأ بصفحة أو اثنتين للاختبار
391
+ - **أنواع النصوص**: يعمل بشكل ممتاز مع النصوص العربية المطبوعة والمكتوبة بوضوح
392
+ """)
393
+
394
+ # ربط الأحداث
395
+ process_btn.click(
396
+ fn=ocr_pdf,
397
+ inputs=[pdf_input, dpi_slider, pages_limit],
398
+ outputs=output_text,
399
+ api_name="ocr_pdf"
400
+ )
401
 
402
+ # إعداد الـ queue
403
+ demo.queue(
404
+ concurrency_count=2, # عدد العمليات المتزامنة
405
+ max_size=10 # حد أقصى للطابور
406
+ )
407
 
408
  if __name__ == "__main__":
409
+ demo.launch(
410
+ server_name="0.0.0.0",
411
+ server_port=7860,
412
+ share=False,
413
+ show_error=True,
414
+ quiet=False
415
+ )