comparison sat/plugins/plugin_merge_req_mercurial.py @ 2793:181735d1b062

plugin mr mercurial, tools(common/utils): moved command protocol to a new module
author Goffi <goffi@goffi.org>
date Fri, 25 Jan 2019 09:06:29 +0100
parents 003b8b4b56a7
children ab2696e34d29
comparison
equal deleted inserted replaced
2792:441b536e28ed 2793:181735d1b062
15 # GNU Affero General Public License for more details. 15 # GNU Affero General Public License for more details.
16 16
17 # You should have received a copy of the GNU Affero General Public License 17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19
20 import re
21 from twisted.python.procutils import which
22 from sat.tools.common import async_process
23 from sat.tools import utils
20 from sat.core.i18n import _, D_ 24 from sat.core.i18n import _, D_
21 from sat.core.constants import Const as C 25 from sat.core.constants import Const as C
22 from sat.core import exceptions 26 from sat.core import exceptions
23 from twisted.internet import reactor, defer, protocol
24 from twisted.python.failure import Failure
25 from twisted.python.procutils import which
26 import re
27 from sat.core.log import getLogger 27 from sat.core.log import getLogger
28 log = getLogger(__name__) 28 log = getLogger(__name__)
29 29
30 30
31 PLUGIN_INFO = { 31 PLUGIN_INFO = {
40 40
41 SHORT_DESC = D_(u"handle Mercurial repository") 41 SHORT_DESC = D_(u"handle Mercurial repository")
42 CLEAN_RE = re.compile(ur'[^\w -._]', flags=re.UNICODE) 42 CLEAN_RE = re.compile(ur'[^\w -._]', flags=re.UNICODE)
43 43
44 44
45 class MercurialProtocol(protocol.ProcessProtocol): 45 class MercurialProtocol(async_process.CommandProtocol):
46 """handle hg commands""" 46 """handle hg commands"""
47 hg = None 47 name = u"Mercurial"
48 48 command = None
49 def __init__(self, deferred, stdin=None):
50 """
51 @param deferred(defer.Deferred): will be called when command is completed
52 @param stdin(str, None): if not None, will be push to standard input
53 """
54 self._stdin = stdin
55 self._deferred = deferred
56 self.data = []
57
58 def connectionMade(self):
59 if self._stdin is not None:
60 self.transport.write(self._stdin)
61 self.transport.closeStdin()
62
63 def outReceived(self, data):
64 self.data.append(data)
65
66 def errReceived(self, data):
67 self.data.append(data)
68
69 def processEnded(self, reason):
70 data = u''.join([d.decode('utf-8') for d in self.data])
71 if (reason.value.exitCode == 0):
72 log.debug(_('Mercurial command succeed'))
73 self._deferred.callback(data)
74 else:
75 msg = (_(u"Can't complete Mercurial command (error code: {code}): {message}")
76 .format(code = reason.value.exitCode, message = data))
77 log.warning(msg)
78 self._deferred.errback(Failure(RuntimeError(msg)))
79 49
80 @classmethod 50 @classmethod
81 def run(cls, path, command, *args, **kwargs): 51 def run(cls, path, command, *args, **kwargs):
82 """Create a new MercurialRegisterProtocol and execute the given mercurial command. 52 """Create a new MercurialRegisterProtocol and execute the given mercurial command.
83 53
84 @param path(unicode): path to the repository 54 @param path(unicode): path to the repository
85 @param command(unicode): command to run 55 @param command(unicode): hg command to run
86 @param *args(unicode): command arguments
87 @param **kwargs: used because Python2 doesn't handle normal kw args after *args
88 can only be:
89 - stdin(unicode, None): data to push to standard input
90 @return ((D)):
91 """ 56 """
92 stdin = kwargs.pop('stdin', None) 57 assert u"path" not in kwargs
93 if kwargs: 58 kwargs["path"] = path
94 raise exceptions.InternalError(u'only stdin is allowed as keyword argument') 59 # FIXME: we have to use this workaround because Twisted's protocol.ProcessProtocol
95 if stdin is not None: 60 # is not using new style classes. This can be removed once moved to
96 stdin = stdin.encode('utf-8') 61 # Python 3 (super can be used normally then).
97 d = defer.Deferred() 62 d = async_process.CommandProtocol.run.__func__(cls, command, *args, **kwargs)
98 mercurial_prot = MercurialProtocol(d, stdin=stdin) 63 d.addErrback(utils.logError)
99 cmd_args = [cls.hg, command.encode('utf-8')]
100 cmd_args.extend([a.encode('utf-8') for a in args])
101 reactor.spawnProcess(mercurial_prot,
102 cls.hg,
103 cmd_args,
104 path=path.encode('utf-8'))
105 return d 64 return d
106 65
107 66
108 class MercurialHandler(object): 67 class MercurialHandler(object):
109 data_types = (u'mercurial_changeset',) 68 data_types = (u'mercurial_changeset',)
110 69
111 def __init__(self, host): 70 def __init__(self, host):
112 log.info(_(u"Mercurial merge request handler initialization")) 71 log.info(_(u"Mercurial merge request handler initialization"))
113 try: 72 try:
114 MercurialProtocol.hg = which('hg')[0] 73 MercurialProtocol.command = which('hg')[0]
115 except IndexError: 74 except IndexError:
116 raise exceptions.NotFound(_(u"Mercurial executable (hg) not found, " 75 raise exceptions.NotFound(_(u"Mercurial executable (hg) not found, "
117 u"can't use Mercurial handler")) 76 u"can't use Mercurial handler"))
118 self.host = host 77 self.host = host
119 self._m = host.plugins['MERGE_REQUESTS'] 78 self._m = host.plugins['MERGE_REQUESTS']
120 self._m.register('mercurial', self, self.data_types, SHORT_DESC) 79 self._m.register('mercurial', self, self.data_types, SHORT_DESC)
80
121 81
122 def check(self, repository): 82 def check(self, repository):
123 d = MercurialProtocol.run(repository, 'identify') 83 d = MercurialProtocol.run(repository, 'identify')
124 d.addCallback(lambda __: True) 84 d.addCallback(lambda __: True)
125 d.addErrback(lambda __: False) 85 d.addErrback(lambda __: False)