Debug Tools
stdout_replacement Class Reference
Inheritance diagram for stdout_replacement:
Collaboration diagram for stdout_replacement:

Public Member Functions

def lock (cls, logger)
 
def unlock (cls)
 

Public Attributes

 is_active
 

Static Public Attributes

bool is_active = False
 

Detailed Description

    In case of reloading this module, never recapture the current `sys.stdout`.

    When disabling this with unlock, it will only restore the standard behavior of the `stdout`
    stream. However, the attached logger will cannot be detached never because someone else can
    have a reference to its older version. This is why this is a global singleton which can
    never dies.

    How do I duplicate sys.stdout to a log file in python?
    https://stackoverflow.com/questions/616645/how-do-i-duplicate-sys-stdout-to-a-log-file-in-python

    How to redirect stdout and stdout to logger in Python
    https://stackoverflow.com/questions/19425736/how-to-redirect-stdout-and-stdout-to-logger-in-python

    Set a Read-Only Attribute in Python?
    https://stackoverflow.com/questions/24497316/set-a-read-only-attribute-in-python

Definition at line 63 of file stdout_replacement.py.

Member Function Documentation

◆ lock()

def lock (   cls,
  logger 
)
    Attach this singleton logger to the `sys.stdout` permanently.

Definition at line 84 of file stdout_replacement.py.

References TeeNoFile.flush(), stderr_replacement.is_active, stdout_replacement.is_active, stderr_replacement.unlock(), and stdout_replacement.unlock().

84  def lock(cls, logger):
85  """
86  Attach this singleton logger to the `sys.stdout` permanently.
87  """
88  global _stdout_singleton
89  global _stdout_default
90  global _stdout_default_class_type
91 
92  # On Sublime Text, the `sys.__stdout__` is None, because they already replaced `sys.stdout`
93  # by some `_LogWriter()` class, then just save the current one over there.
94  if not sys.__stdout__:
95  sys.__stdout__ = sys.stdout
96 
97  try:
98  _stdout_default
99  _stdout_default_class_type
100 
101  except NameError:
102  # sys.stdout.write( "Assigning sys.stdout to _stdout_default\n" )
103  _stdout_default = sys.stdout
104 
105  _stdout_default_class_type = type( _stdout_default )
106  # sys.stdout.write( "Assigned sys.stdout to _stdout_default: %s, %s\n" % ( _stdout_default, _stdout_default_class_type ) )
107 
108  # Recreate the sys.stdout logger when it was reset by `unlock()`
109  if not cls.is_active:
110  cls.is_active = True
111  _stdout_write = _stdout_default.write
112 
113  logger_call = logger._log_clean
114  clean_formatter = logger.clean_formatter
115 
116  global _sys_stdout_write
117  global _sys_stdout_write_hidden
118 
119  if sys.version_info <= (3,2):
120 
121  def customEmit(self, record):
122  """
123  Emit a record.
124  https://stackoverflow.com/questions/12699645/how-can-i-suppress-newline-in-python-logging-module
125 
126  If a formatter is specified, it is used to format the record.
127  The record is then written to the stream with a trailing newline. If
128  exception information is present, it is formatted using
129  traceback.print_exception and appended to the stream. If the stream
130  has an 'encoding' attribute, it is used to determine how to do the
131  output to the stream.
132  """
133  try:
134  msg = self.format(record)
135  stream = self.stream
136  fs = "%s%s"
137  if not _unicode: #if no unicode support...
138  stream.write(fs % (msg, self.terminator))
139  else:
140  try:
141  if (isinstance(msg, unicode) and
142  getattr(stream, 'encoding', None)):
143  ufs = u'%s%s'
144  try:
145  stream.write(ufs % (msg, self.terminator))
146  except UnicodeEncodeError:
147  #Printing to terminals sometimes fails. For example,
148  #with an encoding of 'cp1251', the above write will
149  #work if written to a stream opened or wrapped by
150  #the codecs module, but fail when writing to a
151  #terminal even when the codepage is set to cp1251.
152  #An extra encoding step seems to be needed.
153  stream.write((ufs % (msg, self.terminator)).encode(stream.encoding))
154  else:
155  stream.write(fs % (msg, self.terminator))
156  except UnicodeError:
157  stream.write(fs % (msg.encode("UTF-8"), self.terminator))
158  self.flush()
159  except (KeyboardInterrupt, SystemExit):
160  raise
161  except:
162  self.handleError(record)
163 
164  logging.StreamHandler.terminator = '\n'
165  setattr(StreamHandler, StreamHandler.emit.__name__, customEmit)
166 
167  # Always recreate/override the internal write function used by `_sys_stdout_write`
168  def _sys_stdout_write_hidden(msg, *args, **kwargs):
169  """
170  Suppress newline in Python logging module
171  https://stackoverflow.com/questions/7168790/suppress-newline-in-python-logging-module
172  """
173 
174  try:
175  file = logger._file
176  _stdout_write( msg, *args, **kwargs )
177 
178  formatter = file.formatter
179  terminator = file.terminator
180 
181  file.formatter = clean_formatter
182  file.terminator = ""
183 
184  kwargs['extra'] = { '_duplicated_from_file': True }
185  logger_call( msg, args, kwargs )
186 
187  file.formatter = formatter
188  file.terminator = terminator
189 
190  except Exception:
191  logger.exception( "Could not write to the file: %s(%s)", file, logger )
192  cls.unlock()
193 
194  # Only create one `_sys_stdout_write` function pointer ever
195  try:
196  _sys_stdout_write
197 
198  except NameError:
199 
200  def _sys_stdout_write(*args, **kwargs):
201  """
202  Hides the actual function pointer. This allow the external function pointer to
203  be cached while the internal written can be exchanged between the standard
204  `sys.stdout.write` and our custom wrapper around it.
205  """
206  _sys_stdout_write_hidden( *args, **kwargs )
207 
208  # sys.stdout.write( "_stdout_default: %s\n" % _stdout_default )
209  # sys.stdout.write( "_stdout_default.__dict__: %s\n" % dir( _stdout_default ) )
210  # sys.stdout.write( "(inspect) _stdout_default.__init__: %s\n" % str( inspect.getfullargspec( _stdout_default.__init__ ) ) )
211 
212  try:
213  # Only create one singleton instance ever
214  # del _stdout_singleton
215  _stdout_singleton
216 
217  except NameError:
218 
219  class stdout_replament_hidden(_stdout_default_class_type):
220  """
221  Which special methods bypasses __getattribute__ in Python?
222  https://stackoverflow.com/questions/12872695/which-special-methods-bypasses-getattribute-in-python
223  """
224 
225  if hasattr( _stdout_default, "__abstractmethods__" ):
226  __abstractmethods__ = _stdout_default.__abstractmethods__
227 
228  if hasattr( _stdout_default, "__base__" ):
229  __base__ = _stdout_default.__base__
230 
231  if hasattr( _stdout_default, "__bases__" ):
232  __bases__ = _stdout_default.__bases__
233 
234  if hasattr( _stdout_default, "__basicsize__" ):
235  __basicsize__ = _stdout_default.__basicsize__
236 
237  if hasattr( _stdout_default, "__call__" ):
238  __call__ = _stdout_default.__call__
239 
240  if hasattr( _stdout_default, "__class__" ):
241  __class__ = _stdout_default.__class__
242 
243  if hasattr( _stdout_default, "__delattr__" ):
244  __delattr__ = _stdout_default.__delattr__
245 
246  if hasattr( _stdout_default, "__dict__" ):
247  __dict__ = _stdout_default.__dict__
248 
249  if hasattr( _stdout_default, "__dictoffset__" ):
250  __dictoffset__ = _stdout_default.__dictoffset__
251 
252  if hasattr( _stdout_default, "__dir__" ):
253  __dir__ = _stdout_default.__dir__
254 
255  if hasattr( _stdout_default, "__doc__" ):
256  __doc__ = _stdout_default.__doc__
257 
258  if hasattr( _stdout_default, "__eq__" ):
259  __eq__ = _stdout_default.__eq__
260 
261  if hasattr( _stdout_default, "__flags__" ):
262  __flags__ = _stdout_default.__flags__
263 
264  if hasattr( _stdout_default, "__format__" ):
265  __format__ = _stdout_default.__format__
266 
267  if hasattr( _stdout_default, "__ge__" ):
268  __ge__ = _stdout_default.__ge__
269 
270  if hasattr( _stdout_default, "__getattribute__" ):
271  __getattribute__ = _stdout_default.__getattribute__
272 
273  if hasattr( _stdout_default, "__gt__" ):
274  __gt__ = _stdout_default.__gt__
275 
276  if hasattr( _stdout_default, "__hash__" ):
277  __hash__ = _stdout_default.__hash__
278 
279  if hasattr( _stdout_default, "__init__" ):
280  __init__ = _stdout_default.__init__
281 
282  if hasattr( _stdout_default, "__init_subclass__" ):
283  __init_subclass__ = _stdout_default.__init_subclass__
284 
285  if hasattr( _stdout_default, "__instancecheck__" ):
286  __instancecheck__ = _stdout_default.__instancecheck__
287 
288  if hasattr( _stdout_default, "__itemsize__" ):
289  __itemsize__ = _stdout_default.__itemsize__
290 
291  if hasattr( _stdout_default, "__le__" ):
292  __le__ = _stdout_default.__le__
293 
294  if hasattr( _stdout_default, "__lt__" ):
295  __lt__ = _stdout_default.__lt__
296 
297  if hasattr( _stdout_default, "__module__" ):
298  __module__ = _stdout_default.__module__
299 
300  if hasattr( _stdout_default, "__mro__" ):
301  __mro__ = _stdout_default.__mro__
302 
303  if hasattr( _stdout_default, "__name__" ):
304  __name__ = _stdout_default.__name__
305 
306  if hasattr( _stdout_default, "__ne__" ):
307  __ne__ = _stdout_default.__ne__
308 
309  if hasattr( _stdout_default, "__new__" ):
310  __new__ = _stdout_default.__new__
311 
312  if hasattr( _stdout_default, "__prepare__" ):
313  __prepare__ = _stdout_default.__prepare__
314 
315  if hasattr( _stdout_default, "__qualname__" ):
316  __qualname__ = _stdout_default.__qualname__
317 
318  if hasattr( _stdout_default, "__reduce__" ):
319  __reduce__ = _stdout_default.__reduce__
320 
321  if hasattr( _stdout_default, "__reduce_ex__" ):
322  __reduce_ex__ = _stdout_default.__reduce_ex__
323 
324  if hasattr( _stdout_default, "__repr__" ):
325  __repr__ = _stdout_default.__repr__
326 
327  if hasattr( _stdout_default, "__setattr__" ):
328  __setattr__ = _stdout_default.__setattr__
329 
330  if hasattr( _stdout_default, "__sizeof__" ):
331  __sizeof__ = _stdout_default.__sizeof__
332 
333  if hasattr( _stdout_default, "__str__" ):
334  __str__ = _stdout_default.__str__
335 
336  if hasattr( _stdout_default, "__subclasscheck__" ):
337  __subclasscheck__ = _stdout_default.__subclasscheck__
338 
339  if hasattr( _stdout_default, "__subclasses__" ):
340  __subclasses__ = _stdout_default.__subclasses__
341 
342  if hasattr( _stdout_default, "__subclasshook__" ):
343  __subclasshook__ = _stdout_default.__subclasshook__
344 
345  if hasattr( _stdout_default, "__text_signature__" ):
346  __text_signature__ = _stdout_default.__text_signature__
347 
348  if hasattr( _stdout_default, "__weakrefoffset__" ):
349  __weakrefoffset__ = _stdout_default.__weakrefoffset__
350 
351  if hasattr( _stdout_default, "mro" ):
352  mro = _stdout_default.mro
353 
354  def __init__(self):
355  """
356  Override any super class `type( _stdout_default )` constructor, so we can
357  instantiate any kind of `sys.stdout` replacement object, in case it was
358  already replaced by something else like on Sublime Text with `_LogWriter()`.
359 
360  Assures all attributes were statically replaced just above. This should happen in case
361  some new attribute is added to the python language.
362 
363  This also ignores the only two methods which are not equal, `__init__()` and `__getattribute__()`.
364  """
365  different_methods = ("__init__", "__class__", "__getattribute__")
366  attributes_to_check = set( dir( object ) + dir( type ) )
367 
368  for attribute in attributes_to_check:
369 
370  if attribute not in different_methods \
371  and hasattr( _stdout_default, attribute ):
372 
373  base_class_attribute = super( _stdout_default_class_type, self ).__getattribute__( attribute )
374  target_class_attribute = _stdout_default.__getattribute__( attribute )
375 
376  if base_class_attribute != target_class_attribute:
377  sys.stdout.write( " The base class attribute `%s` is different from the target class:\n%s\n%s\n\n" % ( attribute, base_class_attribute, target_class_attribute ) )
378 
379  def __getattribute__(self, item):
380  # sys.stdout.write( "__getattribute__, item: %s: %s\n" % ( item, _sys_stdout_write ) )
381 
382  if item == 'write':
383  return _sys_stdout_write
384 
385  try:
386  return _stdout_default.__getattribute__( item )
387 
388  except AttributeError:
389  return super( _stdout_default_class_type, _stdout_default ).__getattribute__( item )
390 
391  # sys.stdout.write( "_stdout_default: %s\n" % _stdout_default )
392  # sys.stdout.write( "inspect.getmro(_stdout_default): %s\n" % str( inspect.getmro( type( _stdout_default ) ) ) )
393  # sys.stdout.write( "inspect.getmro(stdout_replacement): %s\n" % str( inspect.getmro( stdout_replament_hidden ) ) )
394  # sys.stdout.write( "traceback.format_stack():\n%s\n" % "".join( traceback.format_stack() ) )
395  _stdout_singleton = stdout_replament_hidden()
396 
397  # sys.stdout.write( "_stdout_singleton: ")
398  # sys.stdout.write( "%s\n" % _stdout_singleton )
399  sys.stdout = _stdout_singleton
400 
401  # sys.stdout.write( "(inspect) signature: %s\n" % str( inspect.signature( _stdout_singleton.write ) ) )
402  # sys.stdout.write( "(inspect) fullargspec: %s\n" % str( inspect.getfullargspec( _stdout_singleton.write ) ) )
403  # sys.stdout.write( "(_stdout_singleton 6): %s\n" % _stdout_singleton )
404 
405  # sys.stdout.write( "Locking... _sys_stdout_write_hidden: %s\n" % _sys_stdout_write_hidden )
406  return cls
407 
Here is the call graph for this function:

◆ unlock()

def unlock (   cls)
    Detach this `stdout` writer from `sys.stdout` and allow the next call to `lock()` create
    a new writer for the stdout.

Definition at line 409 of file stdout_replacement.py.

References stderr_replacement.is_active, and stdout_replacement.is_active.

Referenced by stdout_replacement.lock().

409  def unlock(cls):
410  """
411  Detach this `stdout` writer from `sys.stdout` and allow the next call to `lock()` create
412  a new writer for the stdout.
413  """
414 
415  if cls.is_active:
416  global _sys_stdout_write_hidden
417  # sys.stdout.write( "Unlocking... _sys_stdout_write_hidden: %s\n" % _sys_stdout_write_hidden )
418 
419  cls.is_active = False
420  _sys_stdout_write_hidden = _stdout_default.write
421 
422 
Here is the caller graph for this function:

The documentation for this class was generated from the following file: