helpers.py revision 11828:36b064696175
16019SN/A#!/usr/bin/env python2 26019SN/A# 312542Sgiacomo.travaglini@arm.com# Copyright (c) 2016 ARM Limited 47102SN/A# All rights reserved 57102SN/A# 67102SN/A# The license below extends only to copyright in the software and shall 77102SN/A# not be construed as granting a license to any other intellectual 87102SN/A# property including but not limited to intellectual property relating 97102SN/A# to a hardware implementation of the functionality of the software 107102SN/A# licensed hereunder. You may use the software subject to the license 117102SN/A# terms below provided that you ensure that this notice is replicated 127102SN/A# unmodified and in its entirety in all distributions of the software, 137102SN/A# modified or unmodified, in source code or in binary form. 147102SN/A# 156019SN/A# Redistribution and use in source and binary forms, with or without 166019SN/A# modification, are permitted provided that the following conditions are 176019SN/A# met: redistributions of source code must retain the above copyright 186019SN/A# notice, this list of conditions and the following disclaimer; 196019SN/A# redistributions in binary form must reproduce the above copyright 206019SN/A# notice, this list of conditions and the following disclaimer in the 216019SN/A# documentation and/or other materials provided with the distribution; 226019SN/A# neither the name of the copyright holders nor the names of its 236019SN/A# contributors may be used to endorse or promote products derived from 246019SN/A# this software without specific prior written permission. 256019SN/A# 266019SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 276019SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 286019SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 296019SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 306019SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 316019SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 326019SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 336019SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 346019SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 356019SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 366019SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 376019SN/A# 386019SN/A# Authors: Andreas Sandberg 396019SN/A 406019SN/Aimport subprocess 416019SN/Afrom threading import Timer 426019SN/Aimport time 436019SN/Aimport re 446019SN/A 456019SN/Aclass CallTimeoutException(Exception): 466019SN/A """Exception that indicates that a process call timed out""" 476019SN/A 486019SN/A def __init__(self, status, stdout, stderr): 496019SN/A self.status = status 506019SN/A self.stdout = stdout 516019SN/A self.stderr = stderr 526310SN/A 537433Sgblack@eecs.umich.educlass ProcessHelper(subprocess.Popen): 547191Sgblack@eecs.umich.edu """Helper class to run child processes. 557191Sgblack@eecs.umich.edu 566268SN/A This class wraps a subprocess.Popen class and adds support for 576268SN/A using it in a with block. When the process goes out of scope, it's 586268SN/A automatically terminated. 596268SN/A 607161Sgblack@eecs.umich.edu with ProcessHelper(["/bin/ls"], stdout=subprocess.PIPE) as p: 617206Sgblack@eecs.umich.edu return p.call() 626019SN/A """ 637129Sgblack@eecs.umich.edu def __init__(self, *args, **kwargs): 646019SN/A super(ProcessHelper, self).__init__(*args, **kwargs) 656268SN/A 667139Sgblack@eecs.umich.edu def _terminate_nicely(self, timeout=5): 677161Sgblack@eecs.umich.edu def on_timeout(): 687161Sgblack@eecs.umich.edu self.kill() 697203Sgblack@eecs.umich.edu 707344SAli.Saidi@ARM.com if self.returncode is not None: 717344SAli.Saidi@ARM.com return self.returncode 727161Sgblack@eecs.umich.edu 737161Sgblack@eecs.umich.edu timer = Timer(timeout, on_timeout) 747161Sgblack@eecs.umich.edu self.terminate() 7512258Sgiacomo.travaglini@arm.com status = self.wait() 767195Sgblack@eecs.umich.edu timer.cancel() 7710037SARM gem5 Developers 7810037SARM gem5 Developers return status 7912542Sgiacomo.travaglini@arm.com 8010037SARM gem5 Developers def __enter__(self): 8110037SARM gem5 Developers return self 826268SN/A 837161Sgblack@eecs.umich.edu def __exit__(self, exc_type, exc_value, traceback): 846268SN/A if self.returncode is None: 856268SN/A self._terminate_nicely() 866268SN/A 876268SN/A def call(self, timeout=0): 887139Sgblack@eecs.umich.edu self._timeout = False 897418Sgblack@eecs.umich.edu def on_timeout(): 906268SN/A self._timeout = True 917120Sgblack@eecs.umich.edu self._terminate_nicely() 926268SN/A 937120Sgblack@eecs.umich.edu status, stdout, stderr = None, None, None 947161Sgblack@eecs.umich.edu timer = Timer(timeout, on_timeout) 957194Sgblack@eecs.umich.edu if timeout: 967210Sgblack@eecs.umich.edu timer.start() 977161Sgblack@eecs.umich.edu 987732SAli.Saidi@ARM.com stdout, stderr = self.communicate() 997732SAli.Saidi@ARM.com status = self.wait() 1007732SAli.Saidi@ARM.com 1017732SAli.Saidi@ARM.com timer.cancel() 1027732SAli.Saidi@ARM.com 1037732SAli.Saidi@ARM.com if self._timeout: 1046269SN/A self._terminate_nicely() 1056268SN/A raise CallTimeoutException(self.returncode, stdout, stderr) 1067134Sgblack@eecs.umich.edu else: 1076268SN/A return status, stdout, stderr 1087152Sgblack@eecs.umich.edu 1097152Sgblack@eecs.umich.educlass FileIgnoreList(object): 1106268SN/A """Helper class to implement file ignore lists. 1116268SN/A 1127334Sgblack@eecs.umich.edu This class implements ignore lists using plain string matching and 11310037SARM gem5 Developers regular expressions. In the simplest use case, rules are created 11410037SARM gem5 Developers statically upon initialization: 11510037SARM gem5 Developers 11610037SARM gem5 Developers ignore_list = FileIgnoreList(name=("ignore_me.txt", ), rex=(r".*~", ) 1176268SN/A 1186268SN/A Ignores can be queried using in the same ways as normal Python 1196743SN/A containers: 1206743SN/A 1217363Sgblack@eecs.umich.edu if file_name in ignore_list: 1226743SN/A print "Ignoring %s" % file_name 1236743SN/A 1247732SAli.Saidi@ARM.com 1257321Sgblack@eecs.umich.edu New rules can be added at runtime by extending the list in the 1268868SMatt.Horsnell@arm.com rules attribute: 1277269Sgblack@eecs.umich.edu 1286743SN/A ignore_list.rules.append(FileIgnoreList.simple("bar.txt")) 1296743SN/A """ 1306743SN/A 1317199Sgblack@eecs.umich.edu @staticmethod 1326743SN/A def simple(r): 1336743SN/A return lambda f: f == r 1346268SN/A 1356268SN/A @staticmethod 1367191Sgblack@eecs.umich.edu def rex(r): 1376019SN/A re_obj = r if hasattr(r, "search") else re.compile(r) 138 return lambda name: re_obj.search(name) 139 140 def __init__(self, names=(), rex=()): 141 self.rules = [ FileIgnoreList.simple(n) for n in names ] + \ 142 [ FileIgnoreList.rex(r) for r in rex ] 143 144 def __contains__(self, name): 145 for rule in self.rules: 146 if rule(name): 147 return True 148 return False 149 150if __name__ == "__main__": 151 # Run internal self tests to ensure that the helpers are working 152 # properly. The expected output when running this script is 153 # "SUCCESS!". 154 155 cmd_foo = [ "/bin/echo", "-n", "foo" ] 156 cmd_sleep = [ "/bin/sleep", "10" ] 157 158 # Test that things don't break if the process hasn't been started 159 with ProcessHelper(cmd_foo) as p: 160 pass 161 162 with ProcessHelper(cmd_foo, stdout=subprocess.PIPE) as p: 163 status, stdout, stderr = p.call() 164 assert stdout == "foo" 165 assert status == 0 166 167 try: 168 with ProcessHelper(cmd_sleep) as p: 169 status, stdout, stderr = p.call(timeout=1) 170 assert False, "Timeout not triggered" 171 except CallTimeoutException: 172 pass 173 174 ignore_list = FileIgnoreList( 175 names=("ignore.txt", "foo/test.txt"), 176 rex=(r"~$", re.compile("^#"))) 177 178 assert "ignore.txt" in ignore_list 179 assert "bar.txt" not in ignore_list 180 assert "foo/test.txt" in ignore_list 181 assert "test.txt" not in ignore_list 182 assert "file1.c~" in ignore_list 183 assert "file1.c" not in ignore_list 184 assert "#foo" in ignore_list 185 assert "foo#" not in ignore_list 186 187 ignore_list.rules.append(FileIgnoreList.simple("bar.txt")) 188 assert "bar.txt" in ignore_list 189 190 print "SUCCESS!" 191