Coverage for sources / mimeogram / display.py: 31%
38 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-18 17:27 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-18 17:27 +0000
1# vim: set filetype=python fileencoding=utf-8:
2# -*- coding: utf-8 -*-
4#============================================================================#
5# #
6# Licensed under the Apache License, Version 2.0 (the "License"); #
7# you may not use this file except in compliance with the License. #
8# You may obtain a copy of the License at #
9# #
10# http://www.apache.org/licenses/LICENSE-2.0 #
11# #
12# Unless required by applicable law or agreed to in writing, software #
13# distributed under the License is distributed on an "AS IS" BASIS, #
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
15# See the License for the specific language governing permissions and #
16# limitations under the License. #
17# #
18#============================================================================#
21''' System pager interaction. '''
24from . import __
27_scribe = __.produce_scribe( __name__ )
30def discover_pager( ) -> __.cabc.Callable[ [ str ], None ]:
31 ''' Discovers pager and returns executor function. '''
32 from shutil import which
33 from subprocess import run
34 pager = __.os.environ.get( 'PAGER', 'less' )
35 for pager_ in ( pager, 'less', 'more' ):
36 if ( pager := which( pager_ ) ):
37 match __.sys.platform:
38 case 'win32':
39 # Windows 'more.com' does not support UTF-8.
40 if pager.lower( ).endswith( '\\more.com' ): continue
41 case _: pass
42 break
43 else: pager = ''
44 if pager:
45 # TODO? async
46 def pager_executor( filename: str ) -> None:
47 ''' Executes pager with file. '''
48 run( ( pager, filename ), check = True ) # noqa: S603
49 return pager_executor
50 # TODO? async
51 def console_display( filename: str ) -> None:
52 ''' Prints file to stdout and waits for ENTER key. '''
53 with open( filename, 'r', encoding = 'utf-8' ) as stream:
54 content = stream.read( )
55 print( f"\n\n{content}\n\n" )
56 if __.sys.stdin.isatty( ): input( "Press Enter to continue..." )
58 _scribe.warning( "Could not find pager program for display." )
59 return console_display
62def display_content(
63 content: str, *,
64 suffix: str = '.txt',
65 pager_discoverer: __.cabc.Callable[
66 [ ], __.cabc.Callable[ [ str ], None ] ] = discover_pager,
67) -> None:
68 ''' Displays content via discovered pager. '''
69 from .exceptions import PagerFailure
70 pager = pager_discoverer( )
71 import tempfile
72 from pathlib import Path
73 # Using delete = False to handle file cleanup manually. This ensures
74 # the file handle is properly closed before the pager attempts to read it,
75 # which is particularly important on Windows where open files cannot be
76 # simultaneously accessed by other processes without a read share.
77 with tempfile.NamedTemporaryFile(
78 mode = 'w', suffix = suffix, delete = False, encoding = 'utf-8'
79 ) as tmp:
80 filename = tmp.name
81 tmp.write( content )
82 try: pager( filename )
83 except Exception as exc: raise PagerFailure( cause = exc ) from exc
84 finally:
85 try: Path( filename ).unlink( )
86 except Exception:
87 _scribe.exception( f"Failed to cleanup {filename}" )