|
@@ -49,6 +49,7 @@ Seems we're already running? (pid: %s)"""
|
|
|
|
|
|
|
|
|
def pyimplementation():
|
|
|
+ """Returns string identifying the current Python implementation."""
|
|
|
if hasattr(_platform, 'python_implementation'):
|
|
|
return _platform.python_implementation()
|
|
|
elif sys.platform.startswith('java'):
|
|
@@ -63,6 +64,12 @@ def pyimplementation():
|
|
|
|
|
|
|
|
|
def _find_option_with_arg(argv, short_opts=None, long_opts=None):
|
|
|
+ """Search argv for option specifying its short and longopt
|
|
|
+ alternatives.
|
|
|
+
|
|
|
+ Returns the value of the option if found.
|
|
|
+
|
|
|
+ """
|
|
|
for i, arg in enumerate(argv):
|
|
|
if arg.startswith('-'):
|
|
|
if long_opts and arg.startswith('--'):
|
|
@@ -75,6 +82,10 @@ def _find_option_with_arg(argv, short_opts=None, long_opts=None):
|
|
|
|
|
|
|
|
|
def maybe_patch_concurrency(argv, short_opts=None, long_opts=None):
|
|
|
+ """With short and long opt alternatives that specify the command-line
|
|
|
+ option to set the pool, this makes sure that anything that needs
|
|
|
+ to be patched is completed as early as possible.
|
|
|
+ (e.g. eventlet/gevent monkey patches)."""
|
|
|
try:
|
|
|
pool = _find_option_with_arg(argv, short_opts, long_opts)
|
|
|
except KeyError:
|
|
@@ -87,7 +98,6 @@ def maybe_patch_concurrency(argv, short_opts=None, long_opts=None):
|
|
|
|
|
|
class LockFailed(Exception):
|
|
|
"""Raised if a pidlock can't be acquired."""
|
|
|
- pass
|
|
|
|
|
|
|
|
|
def get_fdmax(default=None):
|
|
@@ -109,8 +119,9 @@ class Pidfile(object):
|
|
|
|
|
|
This is the type returned by :func:`create_pidlock`.
|
|
|
|
|
|
- **Should not be used directly, use the :func:`create_pidlock`
|
|
|
- context instead**
|
|
|
+ TIP: Use the :func:`create_pidlock` function instead,
|
|
|
+ which is more convenient and also removes stale pidfiles (when
|
|
|
+ the process holding the lock is no longer running).
|
|
|
|
|
|
"""
|
|
|
|
|
@@ -150,7 +161,8 @@ class Pidfile(object):
|
|
|
try:
|
|
|
return int(line.strip())
|
|
|
except ValueError:
|
|
|
- raise ValueError('pidfile %r contents invalid.' % self.path)
|
|
|
+ raise ValueError(
|
|
|
+ 'pidfile %r contents invalid.' % self.path)
|
|
|
|
|
|
def remove(self):
|
|
|
"""Removes the lock."""
|
|
@@ -207,14 +219,15 @@ PIDFile = Pidfile # compat alias
|
|
|
|
|
|
|
|
|
def create_pidlock(pidfile):
|
|
|
- """Create and verify pid file.
|
|
|
+ """Create and verify pidfile.
|
|
|
|
|
|
- If the pid file already exists the program exits with an error message,
|
|
|
- however if the process it refers to is not running anymore, the pid file
|
|
|
+ If the pidfile already exists the program exits with an error message,
|
|
|
+ however if the process it refers to is not running anymore, the pidfile
|
|
|
is deleted and the program continues.
|
|
|
|
|
|
- The caller is responsible for releasing the lock before the program
|
|
|
- exits.
|
|
|
+ This function will automatically install an :mod:`atexit` handler
|
|
|
+ to release the lock at exit, you can skip this by calling
|
|
|
+ :func:`_create_pidlock` instead.
|
|
|
|
|
|
:returns: :class:`Pidfile`.
|
|
|
|
|
@@ -239,6 +252,7 @@ def _create_pidlock(pidfile):
|
|
|
|
|
|
|
|
|
def fileno(f):
|
|
|
+ """Get object fileno, or :const:`None` if not defined."""
|
|
|
try:
|
|
|
return f.fileno()
|
|
|
except AttributeError:
|
|
@@ -247,13 +261,11 @@ def fileno(f):
|
|
|
|
|
|
class DaemonContext(object):
|
|
|
_is_open = False
|
|
|
- workdir = DAEMON_WORKDIR
|
|
|
- umask = DAEMON_UMASK
|
|
|
|
|
|
def __init__(self, pidfile=None, workdir=None, umask=None,
|
|
|
fake=False, **kwargs):
|
|
|
- self.workdir = workdir or self.workdir
|
|
|
- self.umask = self.umask if umask is None else umask
|
|
|
+ self.workdir = workdir or DAEMON_WORKDIR
|
|
|
+ self.umask = DAEMON_UMASK if umask is None else umask
|
|
|
self.fake = fake
|
|
|
self.stdfds = (sys.stdin, sys.stdout, sys.stderr)
|
|
|
|
|
@@ -303,7 +315,7 @@ def detached(logfile=None, pidfile=None, uid=None, gid=None, umask=0,
|
|
|
|
|
|
:keyword logfile: Optional log file. The ability to write to this file
|
|
|
will be verified before the process is detached.
|
|
|
- :keyword pidfile: Optional pid file. The pid file will not be created,
|
|
|
+ :keyword pidfile: Optional pidfile. The pidfile will not be created,
|
|
|
as this is the responsibility of the child. But the process will
|
|
|
exit if the pid lock exists and the pid written is still running.
|
|
|
:keyword uid: Optional user id or user name to change
|
|
@@ -319,7 +331,6 @@ def detached(logfile=None, pidfile=None, uid=None, gid=None, umask=0,
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
- import atexit
|
|
|
from celery.platforms import detached, create_pidlock
|
|
|
|
|
|
with detached(logfile='/var/log/app.log', pidfile='/var/run/app.pid',
|
|
@@ -405,6 +416,7 @@ def _setgroups_hack(groups):
|
|
|
|
|
|
|
|
|
def setgroups(groups):
|
|
|
+ """Set active groups from a list of group ids."""
|
|
|
max_groups = None
|
|
|
try:
|
|
|
max_groups = os.sysconf('SC_NGROUPS_MAX')
|
|
@@ -421,6 +433,8 @@ def setgroups(groups):
|
|
|
|
|
|
|
|
|
def initgroups(uid, gid):
|
|
|
+ """Compat version of :func:`os.initgroups` which was first
|
|
|
+ added to Python 2.7."""
|
|
|
if not pwd: # pragma: no cover
|
|
|
return
|
|
|
username = pwd.getpwuid(uid)[0]
|
|
@@ -431,25 +445,13 @@ def initgroups(uid, gid):
|
|
|
setgroups(groups)
|
|
|
|
|
|
|
|
|
-def setegid(gid):
|
|
|
- """Set effective group id."""
|
|
|
- gid = parse_gid(gid)
|
|
|
- if gid != os.getegid():
|
|
|
- os.setegid(gid)
|
|
|
-
|
|
|
-
|
|
|
-def seteuid(uid):
|
|
|
- """Set effective user id."""
|
|
|
- uid = parse_uid(uid)
|
|
|
- if uid != os.geteuid():
|
|
|
- os.seteuid(uid)
|
|
|
-
|
|
|
-
|
|
|
def setgid(gid):
|
|
|
+ """Version of :func:`os.setgid` supporting group names."""
|
|
|
os.setgid(parse_gid(gid))
|
|
|
|
|
|
|
|
|
def setuid(uid):
|
|
|
+ """Version of :func:`os.setuid` supporting usernames."""
|
|
|
os.setuid(parse_uid(uid))
|
|
|
|
|
|
|
|
@@ -625,7 +627,8 @@ else:
|
|
|
|
|
|
|
|
|
def shellsplit(s, posix=True):
|
|
|
- # posix= option to shlex.split first available in Python 2.6+
|
|
|
+ """Compat. version of :func:`shutil.shellsplit` that supports
|
|
|
+ the ``posix`` option which was first added in Python 2.6."""
|
|
|
lexer = shlex.shlex(s, posix=not IS_WINDOWS)
|
|
|
lexer.whitespace_split = True
|
|
|
lexer.commenters = ''
|
|
@@ -633,17 +636,34 @@ def shellsplit(s, posix=True):
|
|
|
|
|
|
|
|
|
def get_errno(n):
|
|
|
+ """Get errno for string, e.g. ``ENOENT``."""
|
|
|
if isinstance(n, basestring):
|
|
|
return getattr(errno, n)
|
|
|
return n
|
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
-def ignore_errno(*errnos):
|
|
|
+def ignore_errno(*errnos, **kwargs):
|
|
|
+ """Context manager to ignore specific POSIX error codes.
|
|
|
+
|
|
|
+ Takes a list of error codes to ignore, which can be either
|
|
|
+ the name of the code, or the code integer itself::
|
|
|
+
|
|
|
+ >>> with ignore_errno('ENOENT'):
|
|
|
+ ... with open('foo', 'r'):
|
|
|
+ ... return r.read()
|
|
|
+
|
|
|
+ >>> with ignore_errno(errno.ENOENT, errno.EPERM):
|
|
|
+ ... pass
|
|
|
+
|
|
|
+ :keyword types: A tuple of exceptions to ignore (when the errno matches),
|
|
|
+ defaults to :exc:`Exception`.
|
|
|
+ """
|
|
|
+ types = kwargs.get('types') or (Exception, )
|
|
|
errnos = [get_errno(errno) for errno in errnos]
|
|
|
try:
|
|
|
yield
|
|
|
- except Exception, exc:
|
|
|
+ except types, exc:
|
|
|
if not hasattr(exc, 'errno'):
|
|
|
raise
|
|
|
if exc.errno not in errnos:
|