CLI


获取当前时间

import datetime

def get_now():
    return datetime.datetime.now().strftime('%Y-%m-%d %X')

把 n 秒转为合适的时间单位

def timer_unit(s):
    '''将 second 秒转为适合的单位
    '''

    if s <= 1:
        return f'{round(s, 1)}s'

    num, unit = [
        (i, u) for i, u in ((s / 60**i, u) for i, u in enumerate('smhd')) if i >= 1
    ][-1]

    return f'{round(num, 1)}{unit}'

上色

from colorama import Fore, Style

def put_color(string, color, bold=False):
    '''
    give me some color to see :P
    '''

    if color == 'gray':
        COLOR = Style.DIM + Fore.WHITE
    else:
        COLOR = getattr(Fore, color.upper(), "WHITE")

    return f'{Style.BRIGHT if bold else ""}{COLOR}{str(string)}{Style.RESET_ALL}'

自定义 log

import sys

class Logger:
    def __init__(self, filename, stdout=False, verb=2):
        self.stdout = stdout
        self.verbose = verb
        self.filename = filename

    def _log(self, log_content):
        if self.stdout:
            sys.stderr.write('\r'+log_content)
        else:
            with open(self.filename, 'a') as fp:
                fp.write(log_content)

    def log(self, event, level='INFO', return_str=False):
        '''记录日志

        @event: 具体事件
        @filename: 日志文件
        @level: 事件等级
        '''

        levels = {
            'DEBU': 'gray',
            'INFO': 'white',
            'WARN': 'yellow',
            'ERRO': 'red'
        }
        color = levels.get(level, 'white')

        clevel = put_color(level, color, True)
        log_time = put_color(get_now(), 'cyan', True)
        log_filename = put_color(self.filename.split('/')[-1], 'gray', True)

        cevent = put_color(event, color) if color != 'white' else event
        log_content = f'[{log_time}] [{clevel}] {cevent}'
        if self.verbose == 3:
            log_content = f'[{log_time}] [{clevel}] [{log_filename}] {cevent}'

        if not event.endswith('\n'):  # 保证结尾换行
            log_content += '\n'

        if self.verbose == 0:
            pass
        elif self.verbose == 1 and level == 'ERRO':
            self._log(log_content)
        elif self.verbose == 2 and level != 'DEBU':
            self._log(log_content)
        elif self.verbose == 3:
            self._log(log_content)

        if return_str:
            return '\r'+log_content.strip()

以 root 权限重启进程

euid = os.geteuid()
if euid != 0:
    args = ['sudo', sys.executable] + sys.argv + [os.environ]  # type: ignore

    if os.system('sudo -n whoami 1>/dev/null 2>/dev/null') != 0:
        print('需要 root 权限,请输入 root 密码')

    os.execlpe('sudo', *args)

转为后台运行

process = subprocess.Popen(
    f''' bash <<< '{sys.executable} {" ".join(sys.argv)} >> {Path.runtime_error_logfile} 2>&1 &'$'\\n''echo $!' ''',
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    shell=True,
)

try:
    pid = process.stdout.read().decode('utf-8').strip()
    if not pid.isdigit():
        raise RuntimeError(process.stderr.read().decode('utf-8'))

except KeyboardInterrupt:
    print(log(f'User canceled', 'WARN', return_str=True))

except subprocess.TimeoutExpired:
    print(log(f'后台运行失败: 命令行有误', 'ERRO', return_str=True))

except Exception as e:
    print(log(f'后台运行失败: {e}', 'ERRO', return_str=True))
    sys.exit(1)
else:
    print(log(f'后台运行中, pid: {pid}', 'INFO', return_str=True))

sys.exit(0)

用 subprocess.Popen 开个新的进程之后,自己退出,这样新的进程就是孤儿进程了,ppid 变成 1,远程的话断开 ssh 也没事。


Source & Reference