| 5 | import docker |
| 6 | |
| 7 | class DockerExecute: |
| 8 | def __init__(self, image="python:3.10", sources_dirname="sources"): |
| 9 | self.image = image |
| 10 | self.client = docker.from_env() |
| 11 | self.sources_dirname = sources_dirname |
| 12 | self.full_source_path = os.path.join(os.getcwd(), sources_dirname) |
| 13 | self.container = None |
| 14 | |
| 15 | def recreate_container(self): |
| 16 | self.shutdown() |
| 17 | |
| 18 | # Pull the Python image |
| 19 | self.client.images.pull(self.image) |
| 20 | |
| 21 | # Create a container and run an infinite loop to keep it alive |
| 22 | self.container = self.client.containers.run( |
| 23 | self.image, |
| 24 | command="bash -c 'while true; do sleep 1; done'", |
| 25 | volumes={ |
| 26 | self.full_source_path: { |
| 27 | 'bind': f"/{self.sources_dirname}", |
| 28 | 'mode': 'rw' |
| 29 | } |
| 30 | }, |
| 31 | working_dir=f"/{self.sources_dirname}", |
| 32 | stderr=True, |
| 33 | stdout=True, |
| 34 | detach=True, |
| 35 | ) |
| 36 | |
| 37 | def shutdown(self): |
| 38 | if not self.container is None: |
| 39 | self.container.stop() |
| 40 | self.container.remove() |
| 41 | self.container = None |
| 42 | |
| 43 | # Execute a command under `sources` that depends on the given script. By default it runs the given script. |
| 44 | def execute(self, script_filename=None, command=None, timeout=10): |
| 45 | try: |
| 46 | if self.container is None: |
| 47 | self.recreate_container() |
| 48 | |
| 49 | # Define a signal handler for the timeout |
| 50 | def handler(signum, frame): |
| 51 | raise TimeoutError("Execution timed out") |
| 52 | |
| 53 | # Set a signal alarm to raise a TimeoutError after 30 seconds |
| 54 | signal.signal(signal.SIGALRM, handler) |
| 55 | signal.alarm(timeout) |
| 56 | |
| 57 | if command is None: |
| 58 | command = f"python {script_filename}" |
| 59 | |
| 60 | # Run the test code in the existing container |
| 61 | exit_code, output = self.container.exec_run( |
| 62 | command, |
| 63 | workdir=f"/{self.sources_dirname}", |
| 64 | ) |
no outgoing calls