mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
103 lines
3.0 KiB
Python
103 lines
3.0 KiB
Python
import os
|
|
import stat
|
|
import tempfile
|
|
import shutil
|
|
import time
|
|
import sys
|
|
import subprocess
|
|
from contextlib import contextmanager
|
|
from functools import wraps
|
|
|
|
|
|
# Would have liked to use a decorator, but for Python 2 the functools.wraps is not good enough for
|
|
# signature preservation that pytest can figure out what is going on. It would be possible to use
|
|
# the decorator package, but that seemed like a bit too much of a hassle.
|
|
@contextmanager
|
|
def in_fresh_temp_dir():
|
|
old_cwd = os.getcwd()
|
|
with managed_temp_dir('extractor-python-buildtools-test-') as tmp:
|
|
os.chdir(tmp)
|
|
try:
|
|
yield tmp
|
|
finally:
|
|
os.chdir(old_cwd)
|
|
|
|
|
|
@contextmanager
|
|
def managed_temp_dir(prefix=None):
|
|
dir = tempfile.mkdtemp(prefix=prefix)
|
|
try:
|
|
yield dir
|
|
finally:
|
|
rmtree_robust(dir)
|
|
|
|
|
|
def rmtree_robust(dir):
|
|
if is_windows():
|
|
# It's important that the path is a Unicode path on Windows, so
|
|
# that the right system calls get used.
|
|
dir = u'' + dir
|
|
if not dir.startswith("\\\\?\\"):
|
|
dir = "\\\\?\\" + os.path.abspath(dir)
|
|
|
|
# Emulate Python 3 "nonlocal" keyword
|
|
class state: pass
|
|
state.last_failed_delete = None
|
|
|
|
|
|
def _rmtree(path):
|
|
"""wrapper of shutil.rmtree to handle Python 3.12 rename (onerror => onexc)"""
|
|
if sys.version_info >= (3, 12):
|
|
shutil.rmtree(path, onexc=remove_error)
|
|
else:
|
|
shutil.rmtree(path, onerror=remove_error)
|
|
|
|
def remove_error(func, path, excinfo):
|
|
# If we get an error twice in a row for the same path then just give up.
|
|
if state.last_failed_delete == path:
|
|
return
|
|
state.last_failed_delete = path
|
|
|
|
# The problem could be one of permissions, so setting path writable
|
|
# might fix it.
|
|
os.chmod(path, stat.S_IWRITE)
|
|
|
|
# On Windows, we sometimes get errors about directories not being
|
|
# empty, but immediately afterwards they are empty. Waiting a bit
|
|
# might therefore be sufficient.
|
|
t = 0.1
|
|
while (True):
|
|
try:
|
|
if os.path.isdir(path):
|
|
_rmtree(path)
|
|
else:
|
|
os.remove(path)
|
|
except OSError:
|
|
if (t > 1):
|
|
return # Give up
|
|
time.sleep(t)
|
|
t *= 2
|
|
_rmtree(dir)
|
|
# On Windows, attempting to write immediately after deletion may result in
|
|
# an 'access denied' exception, so wait a bit.
|
|
if is_windows():
|
|
time.sleep(0.5)
|
|
|
|
|
|
def is_windows():
|
|
return os.name == 'nt'
|
|
|
|
|
|
@contextmanager
|
|
def copy_repo_dir(repo_dir_in):
|
|
with managed_temp_dir(prefix="extractor-python-buildtools-test-") as tmp:
|
|
repo_dir = os.path.join(tmp, 'repo')
|
|
print('copying', repo_dir_in, 'to', repo_dir)
|
|
shutil.copytree(repo_dir_in, repo_dir, symlinks=True)
|
|
yield repo_dir
|
|
|
|
################################################################################
|
|
|
|
|
|
DEVNULL = subprocess.DEVNULL
|