pandaemonium


Namepandaemonium JSON
Version 0.9.1 PyPI version JSON
download
home_pagehttps://github.com/ethanfurman/pandaemonium
SummaryFramework for writing daemons, with API similar to threading and multiprocessing.
upload_time2023-02-01 04:52:31
maintainer
docs_urlNone
authorEthan Furman
requires_python
licenseBSD License
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            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"
}
        
Elapsed time: 0.03454s