如何通过两步将逃生舱口添加到 Python 运行中

使用装饰器函数创建 Python 应用程序的简单退出——你有一个在无限循环上运行的 Python 脚本/应用程序/函数,或者你有一些功能需要你安全退出程序,如果它需要太长。我们怎样才能安全地退出程序?也许是 ctrl C,但这并不总是最安全的方式——而且非技术性的……

Python 提示和技巧

如何通过两步将逃生舱口添加到 Python 运行中

使用装饰器函数创建 Python 应用程序的简单出口

你有一个在无限循环上运行的 Python 脚本/应用程序/函数,或者你有一些功能需要你在运行时间过长时安全退出程序。我们怎样才能安全地退出程序?也许 ctrl C,但这并不总是最安全的方式——非技术人员可能不知道这一点。也许您有一个启动 ML 建模的函数可能需要很长时间。

在这里,我将给你一个装饰器框架,你可以在任何函数上使用它来添加在你按下指定键时安全停止程序的能力。在我们的例子中,这将是“退出”键。

那么这里的标准是什么?

  • 创建一个可以包裹任何函数的装饰器,这样如果该函数花费的时间太长,我们可以使用转义键安全地停止它

The Github Repository

我已将框架放在 GitHub 存储库中,您可以在此处找到:

我可以参考存储库的各个方面。如果您使用存储库,请从 main.py 和调试模式开始。您可以在 my_app/api_utils.py 中找到逃生舱口

Prerequisite Knowledge

在进入细节之前,我只想介绍一下我使用装饰器函数和装饰器工厂来做到这一点。如果您不确定这些是如何工作的,请在此处阅读更多信息:

在非常高的层次上,装饰器是一个函数,它接受另一个函数并扩展后者函数的行为。

把它想象成一个包装器:

你可以用装饰器包装任何函数,它基本上会在函数周围执行一些东西。因此,每当您调用一个用装饰器包装的函数时,它都会执行装饰器,并且该函数将在装饰器内执行。对于我们的示例,这将更有意义。

Steps

第 1 步:定义装饰器

新建一个模块,在repository中,我把这个模块叫做api_utils.py,我这里定义了decorator函数,叫做escape hat。

# Base Library Packages
import _thread

# Third Party Packages
from pynput import keyboard
from functools import wraps
import sys


def escape_hatch(
    start_message="",
    end_message="",
    keyboard_key=keyboard.Key.esc,
    key_string="Esc",
):
    """Function to decorate API calls as an escape hatch
    Args:
      start_message (str): start message to show decorator started
      end_message (str): end message to show decorator end
      method: method to be decorated - the inner function
      keyboard_key (keyboard.Key or keyboard.KeyCode):
    interrupt key to listen for, in our case is the escape key
      key_string (str): string representation of key to print in message"""

    def decorate(func):
        def keyboard_handler(key, escape_key=keyboard_key):
            if key == escape_key:
                print("    Program terminated by user")
                _thread.interrupt_main()

        @wraps(func)
        def wrapper(*args, **kwargs):
            # Handle keyboard interrupts by user
            with keyboard.Listener(on_press=keyboard_handler):
                print(start_message)
                print(
                    f"    Press '{key_string}' any time to terminate the program",
                )
                # Do inner function
                result = func(*args, **kwargs)

                # Print message after API response received
                print()(end_message)
            return result

        return wrapper

    return decorate

上面的函数是我们的装饰器,我们可以将它包裹在任何函数上。如果我启动一个由这个装饰器包装的函数,每当我按下转义键时,它都会停止程序(杀死主线程)。

需要注意的几点:

  • 第 34 行表示一个上下文管理器,它监听任何键盘操作,如果发生,它将启动第 26 行,这会停止程序
  • 第 40 行是执行函数发生的地方

第 2 步:使用我们的装饰器功能

现在我们如何使用它?就像这样:

注意 – 如果您在存储库中关注此文件,则该文件位于 my_app/module_1.py

import time
from .api_utils import escape_hatch


@escape_hatch(start_message="I'm an escape hatch", end_message="Function Finished")
def function_that_runs_forever():
    """I'm a function that runs forever"""

    while 2 > 1:
        print("Just_Running")
        time.sleep(5)


def run():
    function_that_runs_forever()

就像上面一样简单,现在每当你想使用逃生舱口时,只需像上面第 5 行的任何函数之前一样简单地包装它。之后使用 @escape_hatch 和函数。开始消息和结束消息是可选的。

Function_that_runs_forever() 是一个永远运行的函数,用于测试逃生舱口。让我们测试一下:

我故意将一些消息移到了右边,这样我们就可以清楚地看到哪一部分是装饰器包装器。

So what happened?

  1. 逃生舱口装饰器启动,打印出一些东西,它会听我们的键盘,看看我们是否按下了逃生键。
  2. 我们实际的 function_that_runs_forever() 开始运行。这将打印 Just_Running 并且将永远持续下去,除非我停止它。
  3. 然后我按下退出键,装饰器停止该功能并打印用户终止的程序。

给你!功能性逃生舱口。

第 3 步额外:捕捉键盘中断

有时,根据您的应用程序的功能,使用键盘终止主线程有时会引发 KeyboardInterrupt 异常并输出回溯。输出回溯可能不是您想要的。要解决这个问题,您可以添加一个尝试,除了我在存储库的 main.py 中所做的:

from my_app import module_1
import sys

if __name__ == "__main__":
    """
    Main function that runs the application
    """
    try:
        module_1.run()

    except KeyboardInterrupt:
        sys.exit(0)

    except Exception:
        raise Exception

注意在第 11 行我发现了那个异常?

Final Words

希望以上内容可以帮助您更好地理解装饰器功能,并允许您将逃生舱口应用于容易运行太长时间的功能。您可以操纵装饰器以用于不同的键,甚至可能是其他东西来启动停止。这一切都在您的指尖。

如果您发现这有帮助或有见地,请务必鼓掌并关注!

感谢 Marcell Orban 与我一起为我们的应用程序编写框架。

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2022年5月11日
下一篇 2022年5月11日

相关推荐