pandaemonium
============
[n. the abode of all the daemons][1]
pandaemonium provides a framework for writing daemons in Python. The API is
based on the [threading]/[multiprocessing] model, so the primary way
of creating your own daemon is to either subclass and override the `run`
method, or provide a function as the `target` to the `Daemon` class.
Besides `Daemon` there is also a locking pid file -- `PidLockFile`.
`PidLockFile` can either be used manually, or, if a complete path and file
name are provided to `Daemon`, used automatically.
simple usage
------------
from pandaemonium import Daemon
def DoSomethingInteresting():
"Just like it says ;)"
pass
daemon = Daemon(target=DoSomethingInteresting)
daemon.start()
#
# daemon.output will contain any stdout output generated during the
# daemonizing process, up to the stdin/stdout/stderr redirection
#
# daemon.error contains anything sent to the daemon's stderr -- which
# most likely means the daemon died due to an exception
#
# both can be parsed, examined, ignored, etc.
or:
from pandaemonium import Daemon
class MyDaemon(Daemon):
def run():
# do some interesting stuff
md = MyDaemon().start()
The sequence of events that takes place when `start()` is called (adapted from
The Linux Programming Interface by Michael Kerrisk) is:
- detach from the current process, creating a new session
- turn off core dumps
- set uid and gid
- set umask
- set working directory
- create pid file
- set signal handlers
- close inherited file handles
- redirect stdin/stdout/stderr
If any exceptions occur or if any feedback is generated during the `start`
process it will be available as the `error` and `output` attributes of the
daemon instance, where the parent process can analyze, print, etc before
quiting.
Note: Most guides on writing daemons specify setting the umask to 0, but
this creates a security hole as all files become world readable/writable by
default. Pandaemonium sets the umask to 077, but that can be changed if
desired.
advanced usage
--------------
If more control is needed than what is provided by the parameters of Daemon
then one has a couple options available:
- if certain set up / initialization steps need to happen somewhere in the
`start()` sequence, such as after setting the umask and before changing
the working directory::
Daemon.stage4()
# stages 1-4 have now been completed
# do custom steps here
Daemon.start()
# stages 5-9 have now been completed, and run() called
- one can also override any of the stages in a subclass (make sure and
decorate with `check_stage`):
class MyDaemon(Daemon):
def run(self, ip):
# do stuff
@check_stage
def stage7(self):
# do some custom stuff with signals set up
md = MyDaemon('192.168.11.1')
md.start()
- or, to simplify between foreground and daemon operation:
foreground = sys.argv[2:3] == ['--foreground']
pid_file = PidLockFile('/some/path/to/lock.pid')
pid_file.acquire()
if foreground:
pid_file.seal()
else:
daemon = Daemon()
daemon.pid_file = pid_file
daemon.activate()
# at this point, in either foreground or daemon mode, the pid file has
# been sealed (has our correct pid written to it, and it has been
# closed)
run_main_program()
If one's desire is to start the daemon and automatically have any output
printed to screen, one can use `daemon.report()` which prints whatever was
received from the daemon and then quits.
Daemon
======
`Daemon(target=None, args=None, kwargs=None, working_directory='/', umask=0,
prevent_core=True, process_ids=None, inherit_files=None,
signal_map=None, stdin=None, stdout=None, stderr=None)`
- *target*: function to call when daemonized
- *args*: positional args to provide to target
- *kwargs*: keyword args to provide to target
- *detach*: `None` (default) means figure it out, `True` means yes, `False` means no.
Figuring it out means if the parent process is `init`, or a `super
server`, do not detach
- *working_directory*: directory to change to (relative to chroot, if one is in effect)
- *umask*: mask to use when creating files
- *prevent_core*: prevent core dump files from being created
- *process_ids*: tuple of (uid, gid) to switch process to (use (None, None) to disable)
- *pid_file*: `None` (default), or
a PidLockFile instance, or
the string of where to create a PidLockFile
- *inherit_files*: list of open files or file descriptors to keep open
- *signal_map*: dictionary of signal names or numbers to method names or functions
- *stdin* / *stdout* / *stderr*: streams to map the standard streams to. default
is `None` which is mapped to `os.devnull`
`Daemon.run()`
--------------
Method representing the daemon's activity.
You may override this method in a subclass. The standard `run`
method invokes the callable object passed to the object's constructor as
the `target` argument, if any, with sequential and keyword arguments taken
from the `args` and `kwargs` arguments, respectively.
`Daemon.start()`
----------------
Start the daemon's activity.
This may be called at most once per daemon object. It arranges for the
object's `run` method to be invoked as a daemon process.
`Daemon.monitor()`
------------------
Collects stdout and stderr from Daemon process until stage 9 and attaches
it to the daemon instance as `output` and `error`. Can be overridden
if one wants to do more interesting stuff with the daemon's output
`Daemon.stage[1-9]()`
----------------------
One can override the various stages for even more customizations options.
Make sure and decorate such functions with `check_stage`.
PidLockFile
===========
`PidLockFile(file_name, timeout)`
- *file_name*: full path and name of file to use for locking
- *timeout*: how long to wait before concluding that an existing held lock is
not going to be released (default: -1, meaning conclude immediately)
`PidLockFile.acquire(timeout=None)`
-----------------------------------
attempt to capture the lock file; if timeout is `None` use the time out
specified when PidLockFile was created.
`PidLockFile.seal()`
--------------------
write the current process' PID into the acquired file and close it --
should only be called by the daemon process or the stored PID will not be
correct.
`PidLockFile.release()`
-----------------------
remove the lock file, releasing the lock.
[1]: http://dictionary.reference.com/browse/pandemonium
[threading]: https://docs.python.org/2/library/threading.html#threading.Thread
[multiprocessing]: https://docs.python.org/2/library/multiprocessing.html#multiprocessing.Process
Raw data
{
"_id": null,
"home_page": "https://github.com/ethanfurman/pandaemonium",
"name": "pandaemonium",
"maintainer": "",
"docs_url": null,
"requires_python": "",
"maintainer_email": "",
"keywords": "",
"author": "Ethan Furman",
"author_email": "ethan@stoneleaf.us",
"download_url": "https://files.pythonhosted.org/packages/12/d5/1a9c3af5ede8cd55d3c4513a3701a61f5ae0848fc7ded611b9f05895ebb2/pandaemonium-0.9.1.tar.gz",
"platform": null,
"description": "pandaemonium\n============\n\n[n. the abode of all the daemons][1]\n\npandaemonium provides a framework for writing daemons in Python. The API is\nbased on the [threading]/[multiprocessing] model, so the primary way\nof creating your own daemon is to either subclass and override the `run`\nmethod, or provide a function as the `target` to the `Daemon` class.\n\nBesides `Daemon` there is also a locking pid file -- `PidLockFile`.\n`PidLockFile` can either be used manually, or, if a complete path and file\nname are provided to `Daemon`, used automatically.\n\n\nsimple usage\n------------\n\n from pandaemonium import Daemon\n\n def DoSomethingInteresting():\n \"Just like it says ;)\"\n pass\n\n daemon = Daemon(target=DoSomethingInteresting)\n daemon.start()\n #\n # daemon.output will contain any stdout output generated during the\n # daemonizing process, up to the stdin/stdout/stderr redirection\n #\n # daemon.error contains anything sent to the daemon's stderr -- which\n # most likely means the daemon died due to an exception\n #\n # both can be parsed, examined, ignored, etc.\n\nor:\n\n from pandaemonium import Daemon\n\n class MyDaemon(Daemon):\n def run():\n # do some interesting stuff\n\n md = MyDaemon().start()\n\nThe sequence of events that takes place when `start()` is called (adapted from\nThe Linux Programming Interface by Michael Kerrisk) is:\n\n- detach from the current process, creating a new session\n- turn off core dumps\n- set uid and gid\n- set umask\n- set working directory\n- create pid file\n- set signal handlers\n- close inherited file handles\n- redirect stdin/stdout/stderr\n\nIf any exceptions occur or if any feedback is generated during the `start`\nprocess it will be available as the `error` and `output` attributes of the\ndaemon instance, where the parent process can analyze, print, etc before\nquiting.\n\nNote: Most guides on writing daemons specify setting the umask to 0, but\nthis creates a security hole as all files become world readable/writable by\ndefault. Pandaemonium sets the umask to 077, but that can be changed if\ndesired.\n\n\nadvanced usage\n--------------\n\nIf more control is needed than what is provided by the parameters of Daemon\nthen one has a couple options available:\n\n- if certain set up / initialization steps need to happen somewhere in the\n `start()` sequence, such as after setting the umask and before changing\n the working directory::\n\n Daemon.stage4()\n # stages 1-4 have now been completed\n # do custom steps here\n Daemon.start()\n # stages 5-9 have now been completed, and run() called\n\n- one can also override any of the stages in a subclass (make sure and\n decorate with `check_stage`):\n\n class MyDaemon(Daemon):\n def run(self, ip):\n # do stuff\n @check_stage\n def stage7(self):\n # do some custom stuff with signals set up\n\n md = MyDaemon('192.168.11.1')\n md.start()\n\n- or, to simplify between foreground and daemon operation:\n\n foreground = sys.argv[2:3] == ['--foreground']\n pid_file = PidLockFile('/some/path/to/lock.pid')\n pid_file.acquire()\n if foreground:\n pid_file.seal()\n else:\n daemon = Daemon()\n daemon.pid_file = pid_file\n daemon.activate()\n # at this point, in either foreground or daemon mode, the pid file has\n # been sealed (has our correct pid written to it, and it has been\n # closed)\n run_main_program()\n\nIf one's desire is to start the daemon and automatically have any output\nprinted to screen, one can use `daemon.report()` which prints whatever was\nreceived from the daemon and then quits.\n\n\nDaemon\n======\n\n`Daemon(target=None, args=None, kwargs=None, working_directory='/', umask=0,\nprevent_core=True, process_ids=None, inherit_files=None,\nsignal_map=None, stdin=None, stdout=None, stderr=None)`\n\n- *target*: function to call when daemonized\n\n- *args*: positional args to provide to target\n\n- *kwargs*: keyword args to provide to target\n\n- *detach*: `None` (default) means figure it out, `True` means yes, `False` means no.\n Figuring it out means if the parent process is `init`, or a `super\n server`, do not detach\n\n- *working_directory*: directory to change to (relative to chroot, if one is in effect)\n\n- *umask*: mask to use when creating files\n\n- *prevent_core*: prevent core dump files from being created\n\n- *process_ids*: tuple of (uid, gid) to switch process to (use (None, None) to disable)\n\n- *pid_file*: `None` (default), or\n a PidLockFile instance, or\n the string of where to create a PidLockFile\n\n- *inherit_files*: list of open files or file descriptors to keep open\n\n- *signal_map*: dictionary of signal names or numbers to method names or functions\n\n- *stdin* / *stdout* / *stderr*: streams to map the standard streams to. default\n is `None` which is mapped to `os.devnull`\n\n\n`Daemon.run()`\n--------------\nMethod representing the daemon's activity.\n\nYou may override this method in a subclass. The standard `run`\nmethod invokes the callable object passed to the object's constructor as\nthe `target` argument, if any, with sequential and keyword arguments taken\nfrom the `args` and `kwargs` arguments, respectively.\n\n`Daemon.start()`\n----------------\nStart the daemon's activity.\n\nThis may be called at most once per daemon object. It arranges for the\nobject's `run` method to be invoked as a daemon process.\n\n`Daemon.monitor()`\n------------------\nCollects stdout and stderr from Daemon process until stage 9 and attaches\nit to the daemon instance as `output` and `error`. Can be overridden\nif one wants to do more interesting stuff with the daemon's output\n\n`Daemon.stage[1-9]()`\n----------------------\nOne can override the various stages for even more customizations options.\nMake sure and decorate such functions with `check_stage`.\n\n\nPidLockFile\n===========\n\n`PidLockFile(file_name, timeout)`\n\n- *file_name*: full path and name of file to use for locking\n\n- *timeout*: how long to wait before concluding that an existing held lock is\n not going to be released (default: -1, meaning conclude immediately)\n\n`PidLockFile.acquire(timeout=None)`\n-----------------------------------\nattempt to capture the lock file; if timeout is `None` use the time out\nspecified when PidLockFile was created.\n\n`PidLockFile.seal()`\n--------------------\nwrite the current process' PID into the acquired file and close it --\nshould only be called by the daemon process or the stored PID will not be\ncorrect.\n\n`PidLockFile.release()`\n-----------------------\nremove the lock file, releasing the lock.\n\n\n\n[1]: http://dictionary.reference.com/browse/pandemonium\n[threading]: https://docs.python.org/2/library/threading.html#threading.Thread\n[multiprocessing]: https://docs.python.org/2/library/multiprocessing.html#multiprocessing.Process\n\n\n",
"bugtrack_url": null,
"license": "BSD License",
"summary": "Framework for writing daemons, with API similar to threading and multiprocessing.",
"version": "0.9.1",
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "79d5c5e7ef29c76586b0fce72bfd344161c4970a314751c2050faa3d818c4fae",
"md5": "1f5212375050293cbfccb0dc88668515",
"sha256": "387b7d083812b05b25818e93e668b963d829ae5ee098abfd9f98ad4b9713e185"
},
"downloads": -1,
"filename": "pandaemonium-0.9.1-py2-none-any.whl",
"has_sig": false,
"md5_digest": "1f5212375050293cbfccb0dc88668515",
"packagetype": "bdist_wheel",
"python_version": "py2",
"requires_python": null,
"size": 14663,
"upload_time": "2023-02-01T04:52:28",
"upload_time_iso_8601": "2023-02-01T04:52:28.054342Z",
"url": "https://files.pythonhosted.org/packages/79/d5/c5e7ef29c76586b0fce72bfd344161c4970a314751c2050faa3d818c4fae/pandaemonium-0.9.1-py2-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "6adc364736c0e3b39213b5bad652b5e071a2ffcd2ac975a5fb3e4ba374ba7243",
"md5": "b2479c34d2d6f62dc5c958ece48791e3",
"sha256": "0db1048ff68f41bbf19c7eb639b6178ec56bd8c8fa44d467c5bf3db258f1f684"
},
"downloads": -1,
"filename": "pandaemonium-0.9.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "b2479c34d2d6f62dc5c958ece48791e3",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 14662,
"upload_time": "2023-02-01T04:52:34",
"upload_time_iso_8601": "2023-02-01T04:52:34.013388Z",
"url": "https://files.pythonhosted.org/packages/6a/dc/364736c0e3b39213b5bad652b5e071a2ffcd2ac975a5fb3e4ba374ba7243/pandaemonium-0.9.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "12d51a9c3af5ede8cd55d3c4513a3701a61f5ae0848fc7ded611b9f05895ebb2",
"md5": "09540079f3b5b6218059cdbdbdff6738",
"sha256": "9956966ee469953443ba16582bd36e8fce8e9841a0ced881ecf3c1071ab7b5fa"
},
"downloads": -1,
"filename": "pandaemonium-0.9.1.tar.gz",
"has_sig": false,
"md5_digest": "09540079f3b5b6218059cdbdbdff6738",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 17010,
"upload_time": "2023-02-01T04:52:31",
"upload_time_iso_8601": "2023-02-01T04:52:31.134053Z",
"url": "https://files.pythonhosted.org/packages/12/d5/1a9c3af5ede8cd55d3c4513a3701a61f5ae0848fc7ded611b9f05895ebb2/pandaemonium-0.9.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-02-01 04:52:31",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "ethanfurman",
"github_project": "pandaemonium",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "pandaemonium"
}