11#!/usr/bin/env python3
22# -*- coding: utf-8 -*-
3- # Scanner Web Server - Website to talk to scanimage
3+ # Scanner Web Server - Website to talk to SANE scanners
44
5- """Scanner Web Server - Website to talk to scanimage
5+ """Scanner Web Server - Website to talk to SANE scanners
66Copyright (C) 2022 CoolCat467
77
88This program is free software: you can redistribute it and/or modify
2323__title__ = "Sane Scanner Web Server"
2424__author__ = "CoolCat467"
2525__version__ = "2.1.0"
26+ __license__ = "GPLv3"
2627
2728
2829import socket
4041from hypercorn .trio import serve
4142from jinja2 import Template
4243from quart import Response , request
43- from quart .templating import render_template as quart_render_template
4444from quart .templating import stream_template as quart_stream_template
4545from quart_trio import QuartTrio
4646from werkzeug import Response as wkresp
4747
48- from sanescansrv import htmlgen
48+ from sanescansrv import htmlgen , logger
4949from sanescansrv .logger import log
5050
51+ # For some reason error class is not exposed nicely; Let's fix that
5152SaneError = sane ._sane .error
52-
53+ logger . set_title ( __title__ )
5354
5455# Stolen from WOOF (Web Offer One File), Copyright (C) 2004-2009 Simon Budig,
55- # avalable at http://www.home.unix-ag.org/simon/woof
56+ # available at http://www.home.unix-ag.org/simon/woof
5657# with modifications
5758
5859# Utility function to guess the IP (as a string) where the server can be
@@ -107,6 +108,7 @@ def __init__(
107108 self .title = title
108109 self .options = options
109110 self .default = default
111+ self .unit = unit
110112 self .desc = desc
111113 self .set = self .default
112114
@@ -117,37 +119,17 @@ def as_argument(self) -> str:
117119 def __repr__ (self ) -> str :
118120 return (
119121 f"DeviceSetting({ self .name !r} , { self .title !r} , "
120- f"{ self .options !r} , { self .default !r} , { self .desc !r} )"
122+ f"{ self .options !r} , { self .default !r} , { self .unit !r} , "
123+ f"{ self .desc !r} )"
121124 )
122125
123126
124- app : Final = QuartTrio (
127+ app : Final = QuartTrio ( # pylint: disable=invalid-name
125128 __name__ ,
126129 static_folder = "static" ,
127130 template_folder = "templates" ,
128- ) # pylint: disable=invalid-name
129- app_storage : Final [dict [str , Any ]] = {} # pylint: disable=invalid-name
130-
131-
132- async def render_template (
133- template_name_or_list : str | list [str ], ** context : Any
134- ) -> str :
135- """Render the template with the context given.
136-
137- Arguments:
138- template_name_or_list: Template name to render of a list of
139- possible template names.
140- context: The variables to pass to the template.
141-
142- Patched to remove blank lines left by jinja statements"""
143- content = await quart_render_template (template_name_or_list , ** context )
144- new_content = []
145- for line in content .splitlines ():
146- new_line = line .rstrip ()
147- if not new_line :
148- continue
149- new_content .append (new_line )
150- return "\n " .join (new_content )
131+ )
132+ APP_STORAGE : Final [dict [str , Any ]] = {}
151133
152134
153135async def stream_template (
@@ -165,7 +147,7 @@ async def stream_template(
165147 context: The variables to make available in the template.
166148
167149 Patched to remove blank lines left by jinja statements"""
168- # Generate stream in this async block before context is lost
150+ # Generate stream in this async scope before context is lost
169151 stream = await quart_stream_template (template_name_or_list , ** context )
170152
171153 # Create async generator filter
@@ -220,7 +202,7 @@ def get_device_settings(device_addr: str) -> list[DeviceSetting]:
220202 default = "None"
221203 try :
222204 default = str (getattr (device , option .py_name ))
223- except Exception :
205+ except ( AttributeError , ValueError ) :
224206 pass
225207 unit = sane .UNIT_STR [option .unit ].removeprefix ("UNIT_" )
226208
@@ -245,7 +227,7 @@ def display_progress(current: int, total: int) -> None:
245227
246228
247229def preform_scan (device_name : str , out_type : str = "png" ) -> str :
248- "Scan using device and return path."
230+ """ Scan using device and return path."" "
249231 if out_type not in {"pnm" , "tiff" , "png" , "jpeg" }:
250232 raise ValueError ("Output type must be pnm, tiff, png, or jpeg" )
251233 filename = f"scan.{ out_type } "
@@ -255,7 +237,7 @@ def preform_scan(device_name: str, out_type: str = "png") -> str:
255237 ints = {"TYPE_BOOL" , "TYPE_INT" }
256238
257239 with sane .open (device_name ) as device :
258- for setting in app_storage ["device_settings" ][device_name ]:
240+ for setting in APP_STORAGE ["device_settings" ][device_name ]:
259241 name = setting .name .replace ("-" , "_" )
260242 value : str | int = setting .set
261243 if sane .TYPE_STR [device [name ].type ] in ints :
@@ -273,16 +255,16 @@ def preform_scan(device_name: str, out_type: str = "png") -> str:
273255
274256@app .get ("/" )
275257async def root_get () -> AsyncIterator [str ]:
276- "Main page get request"
258+ """ Main page get request"" "
277259 scanners = {}
278260 default = "none"
279261
280- if app_storage ["scanners" ]:
281- scanners = {k : k for k in app_storage ["scanners" ]}
262+ if APP_STORAGE ["scanners" ]:
263+ scanners = {k : k for k in APP_STORAGE ["scanners" ]}
282264 # Since radio_select_dict is if comparison for
283265 # default, if default device does not exist
284266 # there simply won't be a default shown.
285- default = app_storage ["default_device" ]
267+ default = APP_STORAGE ["default_device" ]
286268
287269 return await stream_template (
288270 "root_get.html.jinja" ,
@@ -293,13 +275,13 @@ async def root_get() -> AsyncIterator[str]:
293275
294276@app .post ("/" )
295277async def root_post () -> Response | wkresp :
296- "Main page post handling"
278+ """ Main page post handling"" "
297279 multi_dict = await request .form
298280 data = multi_dict .to_dict ()
299281
300282 # Validate input
301283 img_format = data .get ("img_format" , "png" )
302- device = app_storage ["scanners" ].get (data .get ("scanner" ), "none" )
284+ device = APP_STORAGE ["scanners" ].get (data .get ("scanner" ), "none" )
303285
304286 if img_format not in {"pnm" , "tiff" , "png" , "jpeg" }:
305287 return app .redirect ("/" )
@@ -313,18 +295,18 @@ async def root_post() -> Response | wkresp:
313295
314296@app .get ("/update_scanners" )
315297async def update_scanners_get () -> wkresp :
316- "Update scanners get handling"
317- app_storage ["scanners" ] = get_devices ()
318- for device in app_storage ["scanners" ].values ():
319- app_storage ["device_settings" ][device ] = get_device_settings (device )
298+ """ Update scanners get handling"" "
299+ APP_STORAGE ["scanners" ] = get_devices ()
300+ for device in APP_STORAGE ["scanners" ].values ():
301+ APP_STORAGE ["device_settings" ][device ] = get_device_settings (device )
320302 return app .redirect ("scanners" )
321303
322304
323305@app .get ("/scanners" )
324306async def scanners_get () -> AsyncIterator [str ]:
325- "Scanners page get handling"
307+ """ Scanners page get handling"" "
326308 scanners = {}
327- for display in app_storage .get ("scanners" , {}):
309+ for display in APP_STORAGE .get ("scanners" , {}):
328310 scanner_url = urlencode ({"scanner" : display })
329311 scanners [f"/settings?{ scanner_url } " ] = display
330312
@@ -335,7 +317,7 @@ async def scanners_get() -> AsyncIterator[str]:
335317
336318
337319def get_setting_radio (setting : DeviceSetting ) -> str :
338- "Return setting radio section"
320+ """ Return setting radio section"" "
339321 options = {x .title (): x for x in setting .options }
340322 if set (options .keys ()) == {"1" , "0" }:
341323 options = {"True" : "1" , "False" : "0" }
@@ -346,14 +328,14 @@ def get_setting_radio(setting: DeviceSetting) -> str:
346328
347329@app .get ("/settings" )
348330async def settings_get () -> AsyncIterator [str ] | wkresp :
349- "Settings page get handling"
331+ """ Settings page get handling"" "
350332 scanner = request .args .get ("scanner" , "none" )
351333
352- if scanner == "none" or scanner not in app_storage ["scanners" ]:
334+ if scanner == "none" or scanner not in APP_STORAGE ["scanners" ]:
353335 return app .redirect ("/scanners" )
354336
355- device = app_storage ["scanners" ][scanner ]
356- scanner_settings = app_storage ["device_settings" ].get (device , [])
337+ device = APP_STORAGE ["scanners" ][scanner ]
338+ scanner_settings = APP_STORAGE ["device_settings" ].get (device , [])
357339
358340 return await stream_template (
359341 "settings_get.html.jinja" ,
@@ -366,14 +348,14 @@ async def settings_get() -> AsyncIterator[str] | wkresp:
366348
367349@app .post ("/settings" )
368350async def settings_post () -> wkresp :
369- "Settings page post handling"
351+ """ Settings page post handling"" "
370352 scanner = request .args .get ("scanner" , "none" )
371353
372- if scanner == "none" or scanner not in app_storage ["scanners" ]:
354+ if scanner == "none" or scanner not in APP_STORAGE ["scanners" ]:
373355 return app .redirect ("/scanners" )
374356
375- device = app_storage ["scanners" ][scanner ]
376- scanner_settings = app_storage ["device_settings" ][device ]
357+ device = APP_STORAGE ["scanners" ][scanner ]
358+ scanner_settings = APP_STORAGE ["device_settings" ][device ]
377359
378360 valid_settings = {
379361 setting .name : idx for idx , setting in enumerate (scanner_settings )
@@ -389,7 +371,7 @@ async def settings_post() -> wkresp:
389371 idx = valid_settings [setting_name ]
390372 if new_value not in scanner_settings [idx ].options :
391373 continue
392- app_storage ["device_settings" ][device ][idx ].set = new_value
374+ APP_STORAGE ["device_settings" ][device ][idx ].set = new_value
393375
394376 # Return to page for that scanner
395377 return app .redirect (request .url )
@@ -423,9 +405,9 @@ async def serve_scanner(
423405
424406 config_obj = Config .from_mapping (config )
425407
426- app_storage ["scanners" ] = {}
427- app_storage ["default_device" ] = device_name
428- app_storage ["device_settings" ] = {}
408+ APP_STORAGE ["scanners" ] = {}
409+ APP_STORAGE ["default_device" ] = device_name
410+ APP_STORAGE ["device_settings" ] = {}
429411
430412 print (f"Serving on http://{ location } \n (CTRL + C to quit)" )
431413
@@ -438,7 +420,7 @@ async def serve_scanner(
438420
439421
440422def run () -> None :
441- "Run scanner server"
423+ """ Run scanner server"" "
442424 root_dir = path .abspath (path .expanduser (path .join ("~" , ".sanescansrv" )))
443425 if not path .exists (root_dir ):
444426 makedirs (root_dir , exist_ok = True )
@@ -500,7 +482,7 @@ def run() -> None:
500482
501483
502484def sane_run () -> None :
503- """Run but also handle initializing and uninitializing SANE"""
485+ """Run but also handle initializing and un-initializing SANE"""
504486 try :
505487 sane .init ()
506488 run ()
0 commit comments