comparison python-daemon-2.0.5/daemon/daemon.py @ 33:7ceb967147c3

start xena with no gui add library files
author jingchunzhu <jingchunzhu@gmail.com>
date Wed, 22 Jul 2015 13:24:44 -0700
parents
children
comparison
equal deleted inserted replaced
32:63b1ba1e3424 33:7ceb967147c3
1 # -*- coding: utf-8 -*-
2
3 # daemon/daemon.py
4 # Part of ‘python-daemon’, an implementation of PEP 3143.
5 #
6 # Copyright © 2008–2015 Ben Finney <ben+python@benfinney.id.au>
7 # Copyright © 2007–2008 Robert Niederreiter, Jens Klein
8 # Copyright © 2004–2005 Chad J. Schroeder
9 # Copyright © 2003 Clark Evans
10 # Copyright © 2002 Noah Spurrier
11 # Copyright © 2001 Jürgen Hermann
12 #
13 # This is free software: you may copy, modify, and/or distribute this work
14 # under the terms of the Apache License, version 2.0 as published by the
15 # Apache Software Foundation.
16 # No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
17
18 """ Daemon process behaviour.
19 """
20
21 from __future__ import (absolute_import, unicode_literals)
22
23 import os
24 import sys
25 import resource
26 import errno
27 import signal
28 import socket
29 import atexit
30 try:
31 # Python 2 has both ‘str’ (bytes) and ‘unicode’ (text).
32 basestring = basestring
33 unicode = unicode
34 except NameError:
35 # Python 3 names the Unicode data type ‘str’.
36 basestring = str
37 unicode = str
38
39
40 class DaemonError(Exception):
41 """ Base exception class for errors from this module. """
42
43 def __init__(self, *args, **kwargs):
44 self._chain_from_context()
45
46 super(DaemonError, self).__init__(*args, **kwargs)
47
48 def _chain_from_context(self):
49 _chain_exception_from_existing_exception_context(self, as_cause=True)
50
51
52 class DaemonOSEnvironmentError(DaemonError, OSError):
53 """ Exception raised when daemon OS environment setup receives error. """
54
55
56 class DaemonProcessDetachError(DaemonError, OSError):
57 """ Exception raised when process detach fails. """
58
59
60 class DaemonContext:
61 """ Context for turning the current program into a daemon process.
62
63 A `DaemonContext` instance represents the behaviour settings and
64 process context for the program when it becomes a daemon. The
65 behaviour and environment is customised by setting options on the
66 instance, before calling the `open` method.
67
68 Each option can be passed as a keyword argument to the `DaemonContext`
69 constructor, or subsequently altered by assigning to an attribute on
70 the instance at any time prior to calling `open`. That is, for
71 options named `wibble` and `wubble`, the following invocation::
72
73 foo = daemon.DaemonContext(wibble=bar, wubble=baz)
74 foo.open()
75
76 is equivalent to::
77
78 foo = daemon.DaemonContext()
79 foo.wibble = bar
80 foo.wubble = baz
81 foo.open()
82
83 The following options are defined.
84
85 `files_preserve`
86 :Default: ``None``
87
88 List of files that should *not* be closed when starting the
89 daemon. If ``None``, all open file descriptors will be closed.
90
91 Elements of the list are file descriptors (as returned by a file
92 object's `fileno()` method) or Python `file` objects. Each
93 specifies a file that is not to be closed during daemon start.
94
95 `chroot_directory`
96 :Default: ``None``
97
98 Full path to a directory to set as the effective root directory of
99 the process. If ``None``, specifies that the root directory is not
100 to be changed.
101
102 `working_directory`
103 :Default: ``'/'``
104
105 Full path of the working directory to which the process should
106 change on daemon start.
107
108 Since a filesystem cannot be unmounted if a process has its
109 current working directory on that filesystem, this should either
110 be left at default or set to a directory that is a sensible “home
111 directory” for the daemon while it is running.
112
113 `umask`
114 :Default: ``0``
115
116 File access creation mask (“umask”) to set for the process on
117 daemon start.
118
119 A daemon should not rely on the parent process's umask value,
120 which is beyond its control and may prevent creating a file with
121 the required access mode. So when the daemon context opens, the
122 umask is set to an explicit known value.
123
124 If the conventional value of 0 is too open, consider setting a
125 value such as 0o022, 0o027, 0o077, or another specific value.
126 Otherwise, ensure the daemon creates every file with an
127 explicit access mode for the purpose.
128
129 `pidfile`
130 :Default: ``None``
131
132 Context manager for a PID lock file. When the daemon context opens
133 and closes, it enters and exits the `pidfile` context manager.
134
135 `detach_process`
136 :Default: ``None``
137
138 If ``True``, detach the process context when opening the daemon
139 context; if ``False``, do not detach.
140
141 If unspecified (``None``) during initialisation of the instance,
142 this will be set to ``True`` by default, and ``False`` only if
143 detaching the process is determined to be redundant; for example,
144 in the case when the process was started by `init`, by `initd`, or
145 by `inetd`.
146
147 `signal_map`
148 :Default: system-dependent
149
150 Mapping from operating system signals to callback actions.
151
152 The mapping is used when the daemon context opens, and determines
153 the action for each signal's signal handler:
154
155 * A value of ``None`` will ignore the signal (by setting the
156 signal action to ``signal.SIG_IGN``).
157
158 * A string value will be used as the name of an attribute on the
159 ``DaemonContext`` instance. The attribute's value will be used
160 as the action for the signal handler.
161
162 * Any other value will be used as the action for the
163 signal handler. See the ``signal.signal`` documentation
164 for details of the signal handler interface.
165
166 The default value depends on which signals are defined on the
167 running system. Each item from the list below whose signal is
168 actually defined in the ``signal`` module will appear in the
169 default map:
170
171 * ``signal.SIGTTIN``: ``None``
172
173 * ``signal.SIGTTOU``: ``None``
174
175 * ``signal.SIGTSTP``: ``None``
176
177 * ``signal.SIGTERM``: ``'terminate'``
178
179 Depending on how the program will interact with its child
180 processes, it may need to specify a signal map that
181 includes the ``signal.SIGCHLD`` signal (received when a
182 child process exits). See the specific operating system's
183 documentation for more detail on how to determine what
184 circumstances dictate the need for signal handlers.
185
186 `uid`
187 :Default: ``os.getuid()``
188
189 `gid`
190 :Default: ``os.getgid()``
191
192 The user ID (“UID”) value and group ID (“GID”) value to switch
193 the process to on daemon start.
194
195 The default values, the real UID and GID of the process, will
196 relinquish any effective privilege elevation inherited by the
197 process.
198
199 `prevent_core`
200 :Default: ``True``
201
202 If true, prevents the generation of core files, in order to avoid
203 leaking sensitive information from daemons run as `root`.
204
205 `stdin`
206 :Default: ``None``
207
208 `stdout`
209 :Default: ``None``
210
211 `stderr`
212 :Default: ``None``
213
214 Each of `stdin`, `stdout`, and `stderr` is a file-like object
215 which will be used as the new file for the standard I/O stream
216 `sys.stdin`, `sys.stdout`, and `sys.stderr` respectively. The file
217 should therefore be open, with a minimum of mode 'r' in the case
218 of `stdin`, and mimimum of mode 'w+' in the case of `stdout` and
219 `stderr`.
220
221 If the object has a `fileno()` method that returns a file
222 descriptor, the corresponding file will be excluded from being
223 closed during daemon start (that is, it will be treated as though
224 it were listed in `files_preserve`).
225
226 If ``None``, the corresponding system stream is re-bound to the
227 file named by `os.devnull`.
228
229 """
230
231 __metaclass__ = type
232
233 def __init__(
234 self,
235 chroot_directory=None,
236 working_directory="/",
237 umask=0,
238 uid=None,
239 gid=None,
240 prevent_core=True,
241 detach_process=None,
242 files_preserve=None,
243 pidfile=None,
244 stdin=None,
245 stdout=None,
246 stderr=None,
247 signal_map=None,
248 ):
249 """ Set up a new instance. """
250 self.chroot_directory = chroot_directory
251 self.working_directory = working_directory
252 self.umask = umask
253 self.prevent_core = prevent_core
254 self.files_preserve = files_preserve
255 self.pidfile = pidfile
256 self.stdin = stdin
257 self.stdout = stdout
258 self.stderr = stderr
259
260 if uid is None:
261 uid = os.getuid()
262 self.uid = uid
263 if gid is None:
264 gid = os.getgid()
265 self.gid = gid
266
267 if detach_process is None:
268 detach_process = is_detach_process_context_required()
269 self.detach_process = detach_process
270
271 if signal_map is None:
272 signal_map = make_default_signal_map()
273 self.signal_map = signal_map
274
275 self._is_open = False
276
277 @property
278 def is_open(self):
279 """ ``True`` if the instance is currently open. """
280 return self._is_open
281
282 def open(self):
283 """ Become a daemon process.
284
285 :return: ``None``.
286
287 Open the daemon context, turning the current program into a daemon
288 process. This performs the following steps:
289
290 * If this instance's `is_open` property is true, return
291 immediately. This makes it safe to call `open` multiple times on
292 an instance.
293
294 * If the `prevent_core` attribute is true, set the resource limits
295 for the process to prevent any core dump from the process.
296
297 * If the `chroot_directory` attribute is not ``None``, set the
298 effective root directory of the process to that directory (via
299 `os.chroot`).
300
301 This allows running the daemon process inside a “chroot gaol”
302 as a means of limiting the system's exposure to rogue behaviour
303 by the process. Note that the specified directory needs to
304 already be set up for this purpose.
305
306 * Set the process UID and GID to the `uid` and `gid` attribute
307 values.
308
309 * Close all open file descriptors. This excludes those listed in
310 the `files_preserve` attribute, and those that correspond to the
311 `stdin`, `stdout`, or `stderr` attributes.
312
313 * Change current working directory to the path specified by the
314 `working_directory` attribute.
315
316 * Reset the file access creation mask to the value specified by
317 the `umask` attribute.
318
319 * If the `detach_process` option is true, detach the current
320 process into its own process group, and disassociate from any
321 controlling terminal.
322
323 * Set signal handlers as specified by the `signal_map` attribute.
324
325 * If any of the attributes `stdin`, `stdout`, `stderr` are not
326 ``None``, bind the system streams `sys.stdin`, `sys.stdout`,
327 and/or `sys.stderr` to the files represented by the
328 corresponding attributes. Where the attribute has a file
329 descriptor, the descriptor is duplicated (instead of re-binding
330 the name).
331
332 * If the `pidfile` attribute is not ``None``, enter its context
333 manager.
334
335 * Mark this instance as open (for the purpose of future `open` and
336 `close` calls).
337
338 * Register the `close` method to be called during Python's exit
339 processing.
340
341 When the function returns, the running program is a daemon
342 process.
343
344 """
345 if self.is_open:
346 return
347
348 if self.chroot_directory is not None:
349 change_root_directory(self.chroot_directory)
350
351 if self.prevent_core:
352 prevent_core_dump()
353
354 change_file_creation_mask(self.umask)
355 change_working_directory(self.working_directory)
356 change_process_owner(self.uid, self.gid)
357
358 if self.detach_process:
359 detach_process_context()
360
361 signal_handler_map = self._make_signal_handler_map()
362 set_signal_handlers(signal_handler_map)
363
364 exclude_fds = self._get_exclude_file_descriptors()
365 close_all_open_files(exclude=exclude_fds)
366
367 redirect_stream(sys.stdin, self.stdin)
368 redirect_stream(sys.stdout, self.stdout)
369 redirect_stream(sys.stderr, self.stderr)
370
371 if self.pidfile is not None:
372 self.pidfile.__enter__()
373
374 self._is_open = True
375
376 register_atexit_function(self.close)
377
378 def __enter__(self):
379 """ Context manager entry point. """
380 self.open()
381 return self
382
383 def close(self):
384 """ Exit the daemon process context.
385
386 :return: ``None``.
387
388 Close the daemon context. This performs the following steps:
389
390 * If this instance's `is_open` property is false, return
391 immediately. This makes it safe to call `close` multiple times
392 on an instance.
393
394 * If the `pidfile` attribute is not ``None``, exit its context
395 manager.
396
397 * Mark this instance as closed (for the purpose of future `open`
398 and `close` calls).
399
400 """
401 if not self.is_open:
402 return
403
404 if self.pidfile is not None:
405 # Follow the interface for telling a context manager to exit,
406 # <URL:http://docs.python.org/library/stdtypes.html#typecontextmanager>.
407 self.pidfile.__exit__(None, None, None)
408
409 self._is_open = False
410
411 def __exit__(self, exc_type, exc_value, traceback):
412 """ Context manager exit point. """
413 self.close()
414
415 def terminate(self, signal_number, stack_frame):
416 """ Signal handler for end-process signals.
417
418 :param signal_number: The OS signal number received.
419 :param stack_frame: The frame object at the point the
420 signal was received.
421 :return: ``None``.
422
423 Signal handler for the ``signal.SIGTERM`` signal. Performs the
424 following step:
425
426 * Raise a ``SystemExit`` exception explaining the signal.
427
428 """
429 exception = SystemExit(
430 "Terminating on signal {signal_number!r}".format(
431 signal_number=signal_number))
432 raise exception
433
434 def _get_exclude_file_descriptors(self):
435 """ Get the set of file descriptors to exclude closing.
436
437 :return: A set containing the file descriptors for the
438 files to be preserved.
439
440 The file descriptors to be preserved are those from the
441 items in `files_preserve`, and also each of `stdin`,
442 `stdout`, and `stderr`. For each item:
443
444 * If the item is ``None``, it is omitted from the return
445 set.
446
447 * If the item's ``fileno()`` method returns a value, that
448 value is in the return set.
449
450 * Otherwise, the item is in the return set verbatim.
451
452 """
453 files_preserve = self.files_preserve
454 if files_preserve is None:
455 files_preserve = []
456 files_preserve.extend(
457 item for item in [self.stdin, self.stdout, self.stderr]
458 if hasattr(item, 'fileno'))
459
460 exclude_descriptors = set()
461 for item in files_preserve:
462 if item is None:
463 continue
464 file_descriptor = _get_file_descriptor(item)
465 if file_descriptor is not None:
466 exclude_descriptors.add(file_descriptor)
467 else:
468 exclude_descriptors.add(item)
469
470 return exclude_descriptors
471
472 def _make_signal_handler(self, target):
473 """ Make the signal handler for a specified target object.
474
475 :param target: A specification of the target for the
476 handler; see below.
477 :return: The value for use by `signal.signal()`.
478
479 If `target` is ``None``, return ``signal.SIG_IGN``. If `target`
480 is a text string, return the attribute of this instance named
481 by that string. Otherwise, return `target` itself.
482
483 """
484 if target is None:
485 result = signal.SIG_IGN
486 elif isinstance(target, unicode):
487 name = target
488 result = getattr(self, name)
489 else:
490 result = target
491
492 return result
493
494 def _make_signal_handler_map(self):
495 """ Make the map from signals to handlers for this instance.
496
497 :return: The constructed signal map for this instance.
498
499 Construct a map from signal numbers to handlers for this
500 context instance, suitable for passing to
501 `set_signal_handlers`.
502
503 """
504 signal_handler_map = dict(
505 (signal_number, self._make_signal_handler(target))
506 for (signal_number, target) in self.signal_map.items())
507 return signal_handler_map
508
509
510 def _get_file_descriptor(obj):
511 """ Get the file descriptor, if the object has one.
512
513 :param obj: The object expected to be a file-like object.
514 :return: The file descriptor iff the file supports it; otherwise
515 ``None``.
516
517 The object may be a non-file object. It may also be a
518 file-like object with no support for a file descriptor. In
519 either case, return ``None``.
520
521 """
522 file_descriptor = None
523 if hasattr(obj, 'fileno'):
524 try:
525 file_descriptor = obj.fileno()
526 except ValueError:
527 # The item doesn't support a file descriptor.
528 pass
529
530 return file_descriptor
531
532
533 def change_working_directory(directory):
534 """ Change the working directory of this process.
535
536 :param directory: The target directory path.
537 :return: ``None``.
538
539 """
540 try:
541 os.chdir(directory)
542 except Exception as exc:
543 error = DaemonOSEnvironmentError(
544 "Unable to change working directory ({exc})".format(exc=exc))
545 raise error
546
547
548 def change_root_directory(directory):
549 """ Change the root directory of this process.
550
551 :param directory: The target directory path.
552 :return: ``None``.
553
554 Set the current working directory, then the process root directory,
555 to the specified `directory`. Requires appropriate OS privileges
556 for this process.
557
558 """
559 try:
560 os.chdir(directory)
561 os.chroot(directory)
562 except Exception as exc:
563 error = DaemonOSEnvironmentError(
564 "Unable to change root directory ({exc})".format(exc=exc))
565 raise error
566
567
568 def change_file_creation_mask(mask):
569 """ Change the file creation mask for this process.
570
571 :param mask: The numeric file creation mask to set.
572 :return: ``None``.
573
574 """
575 try:
576 os.umask(mask)
577 except Exception as exc:
578 error = DaemonOSEnvironmentError(
579 "Unable to change file creation mask ({exc})".format(exc=exc))
580 raise error
581
582
583 def change_process_owner(uid, gid):
584 """ Change the owning UID and GID of this process.
585
586 :param uid: The target UID for the daemon process.
587 :param gid: The target GID for the daemon process.
588 :return: ``None``.
589
590 Set the GID then the UID of the process (in that order, to avoid
591 permission errors) to the specified `gid` and `uid` values.
592 Requires appropriate OS privileges for this process.
593
594 """
595 try:
596 os.setgid(gid)
597 os.setuid(uid)
598 except Exception as exc:
599 error = DaemonOSEnvironmentError(
600 "Unable to change process owner ({exc})".format(exc=exc))
601 raise error
602
603
604 def prevent_core_dump():
605 """ Prevent this process from generating a core dump.
606
607 :return: ``None``.
608
609 Set the soft and hard limits for core dump size to zero. On Unix,
610 this entirely prevents the process from creating core dump.
611
612 """
613 core_resource = resource.RLIMIT_CORE
614
615 try:
616 # Ensure the resource limit exists on this platform, by requesting
617 # its current value.
618 core_limit_prev = resource.getrlimit(core_resource)
619 except ValueError as exc:
620 error = DaemonOSEnvironmentError(
621 "System does not support RLIMIT_CORE resource limit"
622 " ({exc})".format(exc=exc))
623 raise error
624
625 # Set hard and soft limits to zero, i.e. no core dump at all.
626 core_limit = (0, 0)
627 resource.setrlimit(core_resource, core_limit)
628
629
630 def detach_process_context():
631 """ Detach the process context from parent and session.
632
633 :return: ``None``.
634
635 Detach from the parent process and session group, allowing the
636 parent to exit while this process continues running.
637
638 Reference: “Advanced Programming in the Unix Environment”,
639 section 13.3, by W. Richard Stevens, published 1993 by
640 Addison-Wesley.
641
642 """
643
644 def fork_then_exit_parent(error_message):
645 """ Fork a child process, then exit the parent process.
646
647 :param error_message: Message for the exception in case of a
648 detach failure.
649 :return: ``None``.
650 :raise DaemonProcessDetachError: If the fork fails.
651
652 """
653 try:
654 pid = os.fork()
655 if pid > 0:
656 os._exit(0)
657 except OSError as exc:
658 error = DaemonProcessDetachError(
659 "{message}: [{exc.errno:d}] {exc.strerror}".format(
660 message=error_message, exc=exc))
661 raise error
662
663 fork_then_exit_parent(error_message="Failed first fork")
664 os.setsid()
665 fork_then_exit_parent(error_message="Failed second fork")
666
667
668 def is_process_started_by_init():
669 """ Determine whether the current process is started by `init`.
670
671 :return: ``True`` iff the parent process is `init`; otherwise
672 ``False``.
673
674 The `init` process is the one with process ID of 1.
675
676 """
677 result = False
678
679 init_pid = 1
680 if os.getppid() == init_pid:
681 result = True
682
683 return result
684
685
686 def is_socket(fd):
687 """ Determine whether the file descriptor is a socket.
688
689 :param fd: The file descriptor to interrogate.
690 :return: ``True`` iff the file descriptor is a socket; otherwise
691 ``False``.
692
693 Query the socket type of `fd`. If there is no error, the file is a
694 socket.
695
696 """
697 result = False
698
699 file_socket = socket.fromfd(fd, socket.AF_INET, socket.SOCK_RAW)
700
701 try:
702 socket_type = file_socket.getsockopt(
703 socket.SOL_SOCKET, socket.SO_TYPE)
704 except socket.error as exc:
705 exc_errno = exc.args[0]
706 if exc_errno == errno.ENOTSOCK:
707 # Socket operation on non-socket.
708 pass
709 else:
710 # Some other socket error.
711 result = True
712 else:
713 # No error getting socket type.
714 result = True
715
716 return result
717
718
719 def is_process_started_by_superserver():
720 """ Determine whether the current process is started by the superserver.
721
722 :return: ``True`` if this process was started by the internet
723 superserver; otherwise ``False``.
724
725 The internet superserver creates a network socket, and
726 attaches it to the standard streams of the child process. If
727 that is the case for this process, return ``True``, otherwise
728 ``False``.
729
730 """
731 result = False
732
733 stdin_fd = sys.__stdin__.fileno()
734 if is_socket(stdin_fd):
735 result = True
736
737 return result
738
739
740 def is_detach_process_context_required():
741 """ Determine whether detaching the process context is required.
742
743 :return: ``True`` iff the process is already detached; otherwise
744 ``False``.
745
746 The process environment is interrogated for the following:
747
748 * Process was started by `init`; or
749
750 * Process was started by `inetd`.
751
752 If any of the above are true, the process is deemed to be already
753 detached.
754
755 """
756 result = True
757 if is_process_started_by_init() or is_process_started_by_superserver():
758 result = False
759
760 return result
761
762
763 def close_file_descriptor_if_open(fd):
764 """ Close a file descriptor if already open.
765
766 :param fd: The file descriptor to close.
767 :return: ``None``.
768
769 Close the file descriptor `fd`, suppressing an error in the
770 case the file was not open.
771
772 """
773 try:
774 os.close(fd)
775 except EnvironmentError as exc:
776 if exc.errno == errno.EBADF:
777 # File descriptor was not open.
778 pass
779 else:
780 error = DaemonOSEnvironmentError(
781 "Failed to close file descriptor {fd:d} ({exc})".format(
782 fd=fd, exc=exc))
783 raise error
784
785
786 MAXFD = 2048
787
788 def get_maximum_file_descriptors():
789 """ Get the maximum number of open file descriptors for this process.
790
791 :return: The number (integer) to use as the maximum number of open
792 files for this process.
793
794 The maximum is the process hard resource limit of maximum number of
795 open file descriptors. If the limit is “infinity”, a default value
796 of ``MAXFD`` is returned.
797
798 """
799 limits = resource.getrlimit(resource.RLIMIT_NOFILE)
800 result = limits[1]
801 if result == resource.RLIM_INFINITY:
802 result = MAXFD
803 return result
804
805
806 def close_all_open_files(exclude=set()):
807 """ Close all open file descriptors.
808
809 :param exclude: Collection of file descriptors to skip when closing
810 files.
811 :return: ``None``.
812
813 Closes every file descriptor (if open) of this process. If
814 specified, `exclude` is a set of file descriptors to *not*
815 close.
816
817 """
818 maxfd = get_maximum_file_descriptors()
819 for fd in reversed(range(maxfd)):
820 if fd not in exclude:
821 close_file_descriptor_if_open(fd)
822
823
824 def redirect_stream(system_stream, target_stream):
825 """ Redirect a system stream to a specified file.
826
827 :param standard_stream: A file object representing a standard I/O
828 stream.
829 :param target_stream: The target file object for the redirected
830 stream, or ``None`` to specify the null device.
831 :return: ``None``.
832
833 `system_stream` is a standard system stream such as
834 ``sys.stdout``. `target_stream` is an open file object that
835 should replace the corresponding system stream object.
836
837 If `target_stream` is ``None``, defaults to opening the
838 operating system's null device and using its file descriptor.
839
840 """
841 if target_stream is None:
842 target_fd = os.open(os.devnull, os.O_RDWR)
843 else:
844 target_fd = target_stream.fileno()
845 os.dup2(target_fd, system_stream.fileno())
846
847
848 def make_default_signal_map():
849 """ Make the default signal map for this system.
850
851 :return: A mapping from signal number to handler object.
852
853 The signals available differ by system. The map will not contain
854 any signals not defined on the running system.
855
856 """
857 name_map = {
858 'SIGTSTP': None,
859 'SIGTTIN': None,
860 'SIGTTOU': None,
861 'SIGTERM': 'terminate',
862 }
863 signal_map = dict(
864 (getattr(signal, name), target)
865 for (name, target) in name_map.items()
866 if hasattr(signal, name))
867
868 return signal_map
869
870
871 def set_signal_handlers(signal_handler_map):
872 """ Set the signal handlers as specified.
873
874 :param signal_handler_map: A map from signal number to handler
875 object.
876 :return: ``None``.
877
878 See the `signal` module for details on signal numbers and signal
879 handlers.
880
881 """
882 for (signal_number, handler) in signal_handler_map.items():
883 signal.signal(signal_number, handler)
884
885
886 def register_atexit_function(func):
887 """ Register a function for processing at program exit.
888
889 :param func: A callable function expecting no arguments.
890 :return: ``None``.
891
892 The function `func` is registered for a call with no arguments
893 at program exit.
894
895 """
896 atexit.register(func)
897
898
899 def _chain_exception_from_existing_exception_context(exc, as_cause=False):
900 """ Decorate the specified exception with the existing exception context.
901
902 :param exc: The exception instance to decorate.
903 :param as_cause: If true, the existing context is declared to be
904 the cause of the exception.
905 :return: ``None``.
906
907 :PEP:`344` describes syntax and attributes (`__traceback__`,
908 `__context__`, `__cause__`) for use in exception chaining.
909
910 Python 2 does not have that syntax, so this function decorates
911 the exception with values from the current exception context.
912
913 """
914 (existing_exc_type, existing_exc, existing_traceback) = sys.exc_info()
915 if as_cause:
916 exc.__cause__ = existing_exc
917 else:
918 exc.__context__ = existing_exc
919 exc.__traceback__ = existing_traceback
920
921
922 # Local variables:
923 # coding: utf-8
924 # mode: python
925 # End:
926 # vim: fileencoding=utf-8 filetype=python :