##// END OF EJS Templates
logtoprocess: use lowercase for docstring title
Jun Wu -
r31601:8e7feaad default
parent child Browse files
Show More
@@ -1,131 +1,131
1 1 # logtoprocess.py - send ui.log() data to a subprocess
2 2 #
3 3 # Copyright 2016 Facebook, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 """Send ui.log() data to a subprocess (EXPERIMENTAL)
7 """send ui.log() data to a subprocess (EXPERIMENTAL)
8 8
9 9 This extension lets you specify a shell command per ui.log() event,
10 10 sending all remaining arguments to as environment variables to that command.
11 11
12 12 Each positional argument to the method results in a `MSG[N]` key in the
13 13 environment, starting at 1 (so `MSG1`, `MSG2`, etc.). Each keyword argument
14 14 is set as a `OPT_UPPERCASE_KEY` variable (so the key is uppercased, and
15 15 prefixed with `OPT_`). The original event name is passed in the `EVENT`
16 16 environment variable, and the process ID of mercurial is given in `HGPID`.
17 17
18 18 So given a call `ui.log('foo', 'bar', 'baz', spam='eggs'), a script configured
19 19 for the `foo` event can expect an environment with `MSG1=bar`, `MSG2=baz`, and
20 20 `OPT_SPAM=eggs`.
21 21
22 22 Scripts are configured in the `[logtoprocess]` section, each key an event name.
23 23 For example::
24 24
25 25 [logtoprocess]
26 26 commandexception = echo "$MSG2$MSG3" > /var/log/mercurial_exceptions.log
27 27
28 28 would log the warning message and traceback of any failed command dispatch.
29 29
30 30 Scripts are run asynchronously as detached daemon processes; mercurial will
31 31 not ensure that they exit cleanly.
32 32
33 33 """
34 34
35 35 from __future__ import absolute_import
36 36
37 37 import itertools
38 38 import os
39 39 import platform
40 40 import subprocess
41 41 import sys
42 42
43 43 from mercurial import encoding
44 44
45 45 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
46 46 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
47 47 # be specifying the version(s) of Mercurial they are tested with, or
48 48 # leave the attribute unspecified.
49 49 testedwith = 'ships-with-hg-core'
50 50
51 51 def uisetup(ui):
52 52 if platform.system() == 'Windows':
53 53 # no fork on Windows, but we can create a detached process
54 54 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx
55 55 # No stdlib constant exists for this value
56 56 DETACHED_PROCESS = 0x00000008
57 57 _creationflags = DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP
58 58
59 59 def runshellcommand(script, env):
60 60 # we can't use close_fds *and* redirect stdin. I'm not sure that we
61 61 # need to because the detached process has no console connection.
62 62 subprocess.Popen(
63 63 script, shell=True, env=env, close_fds=True,
64 64 creationflags=_creationflags)
65 65 else:
66 66 def runshellcommand(script, env):
67 67 # double-fork to completely detach from the parent process
68 68 # based on http://code.activestate.com/recipes/278731
69 69 pid = os.fork()
70 70 if pid:
71 71 # parent
72 72 return
73 73 # subprocess.Popen() forks again, all we need to add is
74 74 # flag the new process as a new session.
75 75 if sys.version_info < (3, 2):
76 76 newsession = {'preexec_fn': os.setsid}
77 77 else:
78 78 newsession = {'start_new_session': True}
79 79 try:
80 80 # connect stdin to devnull to make sure the subprocess can't
81 81 # muck up that stream for mercurial.
82 82 subprocess.Popen(
83 83 script, shell=True, stdin=open(os.devnull, 'r'), env=env,
84 84 close_fds=True, **newsession)
85 85 finally:
86 86 # mission accomplished, this child needs to exit and not
87 87 # continue the hg process here.
88 88 os._exit(0)
89 89
90 90 class logtoprocessui(ui.__class__):
91 91 def log(self, event, *msg, **opts):
92 92 """Map log events to external commands
93 93
94 94 Arguments are passed on as environment variables.
95 95
96 96 """
97 97 script = self.config('logtoprocess', event)
98 98 if script:
99 99 if msg:
100 100 # try to format the log message given the remaining
101 101 # arguments
102 102 try:
103 103 # Python string formatting with % either uses a
104 104 # dictionary *or* tuple, but not both. If we have
105 105 # keyword options, assume we need a mapping.
106 106 formatted = msg[0] % (opts or msg[1:])
107 107 except (TypeError, KeyError):
108 108 # Failed to apply the arguments, ignore
109 109 formatted = msg[0]
110 110 messages = (formatted,) + msg[1:]
111 111 else:
112 112 messages = msg
113 113 # positional arguments are listed as MSG[N] keys in the
114 114 # environment
115 115 msgpairs = (
116 116 ('MSG{0:d}'.format(i), str(m))
117 117 for i, m in enumerate(messages, 1))
118 118 # keyword arguments get prefixed with OPT_ and uppercased
119 119 optpairs = (
120 120 ('OPT_{0}'.format(key.upper()), str(value))
121 121 for key, value in opts.iteritems())
122 122 env = dict(itertools.chain(encoding.environ.items(),
123 123 msgpairs, optpairs),
124 124 EVENT=event, HGPID=str(os.getpid()))
125 125 # Connect stdin to /dev/null to prevent child processes messing
126 126 # with mercurial's stdin.
127 127 runshellcommand(script, env)
128 128 return super(logtoprocessui, self).log(event, *msg, **opts)
129 129
130 130 # Replace the class for this instance and all clones created from it:
131 131 ui.__class__ = logtoprocessui
General Comments 0
You need to be logged in to leave comments. Login now