File: safe_print.py

package info (click to toggle)
displaycal-py3 3.9.16-1
  • links: PTS
  • area: main
  • in suites: forky, sid, trixie
  • size: 29,120 kB
  • sloc: python: 115,777; javascript: 11,540; xml: 598; sh: 257; makefile: 173
file content (133 lines) | stat: -rw-r--r-- 4,151 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# -*- coding: utf-8 -*-

import locale
import os
import sys

from DisplayCAL.encoding import get_encodings


original_codepage = None
enc, fs_enc = get_encodings()
_conwidth = None


def _get_console_width():
    global _conwidth
    if _conwidth is None:
        _conwidth = 80
        try:
            if sys.platform == "win32":
                from ctypes import windll, create_string_buffer
                import struct

                # Use stderr handle so that pipes don't affect the reported size
                stderr_handle = windll.kernel32.GetStdHandle(-12)
                buf = create_string_buffer(22)
                consinfo = windll.kernel32.GetConsoleScreenBufferInfo(
                    stderr_handle, buf
                )
                if consinfo:
                    _conwidth = struct.unpack("hhhhHhhhhhh", buf.raw)[0]
            else:
                _conwidth = int(os.getenv("COLUMNS"))
        except Exception:
            pass
    return _conwidth


class SafePrinter:
    def __init__(
        self,
        pad=False,
        padchar=" ",
        sep=" ",
        end="\n",
        file_=sys.stdout,
        fn=None,
        encoding=None,
    ):
        """Write safely, avoiding any UnicodeDe-/EncodingErrors on strings and
        converting all other objects to safe string representations.

        sprint = SafePrinter(pad=False, padchar=' ', sep=' ', end='\\n',
                             file=sys.stdout, fn=None)
        sprint(value, ..., pad=False, padchar=' ', sep=' ', end='\\n',
               file=sys.stdout, fn=None)

        Writes the values to a stream (default sys.stdout), honoring its encoding and
        replacing characters not present in the encoding with question marks silently.

        Optional keyword arguments:
        pad:     pad the lines to n chars, or os.getenv('COLUMNS') if True.
        padchar: character to use for padding, default a space.
        sep:     string inserted between values, default a space.
        end:     string appended after the last value, default a newline.
        file:    a file-like object (stream); defaults to the sys.stdout.
        fn:      a function to execute instead of printing.
        """
        self.pad = pad
        self.padchar = padchar
        self.sep = sep
        self.end = end
        self.file = file_
        self.fn = fn
        self.encoding = "utf-8"

    def __call__(self, *args, **kwargs):
        # TODO: Why calling the instance writes it, this is not a good practice.
        self.write(*args, **kwargs)

    def flush(self):
        self.file and self.file.flush()

    def write(self, *args, **kwargs):
        pad = kwargs.get("pad", self.pad)
        padchar = kwargs.get("padchar", self.padchar)
        sep = kwargs.get("sep", self.sep)
        end = kwargs.get("end", self.end)
        file_ = kwargs.get("file_", self.file)
        fn = kwargs.get("fn", self.fn)
        encoding = self.encoding

        # convert everything to bytes
        if isinstance(padchar, str):
            padchar = padchar.encode(self.encoding)
        if isinstance(sep, str):
            sep = sep.encode(self.encoding)
        if isinstance(end, str):
            end = end.encode(self.encoding)

        strargs = []
        for arg in args:
            if not isinstance(arg, bytes):
                arg = bytes(arg, encoding, "asciize")
            strargs.append(arg)
        line = sep.join(strargs).rstrip(end)
        if pad is not False:
            if pad is True:
                width = _get_console_width()
            else:
                width = int(pad)
            line = line.ljust(width, padchar)
        if fn:
            fn(line)
        else:
            try:
                if "b" not in file_.mode:
                    line = line.decode(self.encoding)
                    if end:
                        end = end.decode(self.encoding)
                file_.write(line)
                if end:
                    file_.write(end)
            except IOError:
                pass


safe_print = SafePrinter()


if __name__ == "__main__":
    for arg in sys.argv[1:]:
        print(arg)