用于检查系统调用结果的Pythonic样式

发布于 2025-02-05 23:35:05 字数 828 浏览 2 评论 0原文

我已经继承了一个基本上是Shell脚本的Python脚本,它使用OS.System()的重复调用来运行一堆命令行实用程序,而无需检查结果。为了使脚本更加坚固,我正在考虑添加类似下面的类方法:

    def subprocess(self, cmd):
        proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        while proc.poll() is None:
            if proc.wait() == 0:
                return True
            else:
                return False

    def check_package_installed(self, pkg):
       cmd = " ".join(["dnf list installed", pkg])
       rv = self.subprocess(cmd)
       if rv != True:
          raise Exception("Package {} not installed.".format(pkg)))

然后,在脚本的主要部分中,类似的是:

try:
    checker.check_package_installed("jdkasjdsa")
except Exception as e:
    print("Error: ",e)
    # recovery code here

这是一种惯用代码任务?

I have inherited a python script that is essentially a shell script, it uses repeated calls to os.system() to run a bunch of command line utilities without necessarily checking the result. In order to make the script a little more rugged, I was thinking of adding class methods like below:

    def subprocess(self, cmd):
        proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        while proc.poll() is None:
            if proc.wait() == 0:
                return True
            else:
                return False

    def check_package_installed(self, pkg):
       cmd = " ".join(["dnf list installed", pkg])
       rv = self.subprocess(cmd)
       if rv != True:
          raise Exception("Package {} not installed.".format(pkg)))

And then, in the main section of the script, something like this:

try:
    checker.check_package_installed("jdkasjdsa")
except Exception as e:
    print("Error: ",e)
    # recovery code here

Is this the kind of idiomatic code another Python programmer would expect for this task?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

西瓜 2025-02-12 23:35:05
  1. 使用异常有点宽。

    让我们假设您的pkg将是整数。然后,加入操作将使用typeerror失败。但是typeError异常的子类,这意味着您将尝试在此处运行恢复代码,即使不同的异常提高

  2. 看起来您为内部用法创建脚本,这不是一个大问题,但是通常将传递给命令的参数

  3. 您的命令结果检查逻辑逻辑很尴尬而脆弱(最重要的问题)

您可以尝试以下代码:

import subprocess
import shlex


# this Exception class allows us to except exact Exceptions only
class PackageNotFoundException(OSError):
  pass


class UnexpectedException(OSError):
  pass


class Checker:

  # using static method we able to run this method without creating instance
  @staticmethod
  def check_package(pkg_name: str):
    # shlex.quote(pkg_name) -> escape command line parameter
    result = subprocess.Popen(
      "/usr/bin/env dnf list installed " + shlex.quote(pkg_name),
      shell=True,
      # hide output
      stdout=subprocess.DEVNULL,
      stderr=subprocess.PIPE
    )

    _, err = result.communicate()
    err = err.decode('utf8').rstrip('\r\n')
    exit_code = result.wait()

    # check exact error (dnf returns 1 if package not found)
    if exit_code == 1 and 'No matching Packages' in err:
      raise PackageNotFoundException(f"Package not found {pkg_name}")
    # if command status code is not 0
    if exit_code != 0:
      raise UnexpectedException(f"Error [{err}] with status code {exit_code} happens when trying to check {pkg_name}")

package = "package name with spaces"

try:
  Checker.check_package(package)
# handle package not found error
except PackageNotFoundException as e:
  print(f"Trying to install package {package}")
  # recovery code here
# re-raise error if unknown (unexpected) error
except Exception as e:
  raise e
  1. Using of Exception is little bit wide.

    Let's suppose that your pkg will be integer. Then join operation will be failed with TypeError. However TypeError is subclass for Exception and that means that you will try to run recovery code here even if different Exception raised

  2. Looks like you creating script for internal usage and that is not a big problem, but generally arguments passed to command should be escaped

  3. Your command result check logic is awkward and fragile (most important problem)

You may try this code:

import subprocess
import shlex


# this Exception class allows us to except exact Exceptions only
class PackageNotFoundException(OSError):
  pass


class UnexpectedException(OSError):
  pass


class Checker:

  # using static method we able to run this method without creating instance
  @staticmethod
  def check_package(pkg_name: str):
    # shlex.quote(pkg_name) -> escape command line parameter
    result = subprocess.Popen(
      "/usr/bin/env dnf list installed " + shlex.quote(pkg_name),
      shell=True,
      # hide output
      stdout=subprocess.DEVNULL,
      stderr=subprocess.PIPE
    )

    _, err = result.communicate()
    err = err.decode('utf8').rstrip('\r\n')
    exit_code = result.wait()

    # check exact error (dnf returns 1 if package not found)
    if exit_code == 1 and 'No matching Packages' in err:
      raise PackageNotFoundException(f"Package not found {pkg_name}")
    # if command status code is not 0
    if exit_code != 0:
      raise UnexpectedException(f"Error [{err}] with status code {exit_code} happens when trying to check {pkg_name}")

package = "package name with spaces"

try:
  Checker.check_package(package)
# handle package not found error
except PackageNotFoundException as e:
  print(f"Trying to install package {package}")
  # recovery code here
# re-raise error if unknown (unexpected) error
except Exception as e:
  raise e
于我来说 2025-02-12 23:35:05

我的答案与 @juanpa.arrivillaga评论相似。您可以使用以下示例使用subprocess.run命令,而不是使用popen。 subprocess.run将等待命令完成,并将返回完整的程序实例。如果您不关心输出,则可以使用Pass Check = true,并且如果命令返回错误代码!= 0,则可以提高子过程。

import subprocess
class CheckProcess:

    def execute_cmd(self, cmd):
        result = False
        try:
            output = subprocess.run(cmd, shell=True, check=True)
            result = True
        except subprocess.CalledProcessError as exc:
            print('Exception hit: %s', exc)
        else:
            return result

    def check_package_installed(self, pkg):
       cmd = " ".join(["dnf list installed", pkg])
       result = self.execute_cmd(cmd)
       if result != True:
          raise Exception("Package {} not installed.".format(pkg))

c = CheckProcess()
c.check_package_installed('cchfhfjgj')

输出

Exception hit: %s Command 'dnf list installed cchfhfjgj' returned non-zero exit status 127.

---------------------------------------------------------------------------

Exception                                 Traceback (most recent call last)

<ipython-input-2-029eeeb6eef7> in <module>()
     19 
     20 c = CheckProcess()
---> 21 c.check_package_installed('cchfhfjgj')

<ipython-input-2-029eeeb6eef7> in check_package_installed(self, pkg)
     16        rv = self.subprocess(cmd)
     17        if rv != True:
---> 18           raise Exception("Package {} not installed.".format(pkg))
     19 
     20 c = CheckProcess()

Exception: Package cchfhfjgj not installed.

请阅读您在系统上使用的Python版本的subprocess.run文档。

My answer was on similar lines as @juanpa.arrivillaga comment. Instead of using Popen you can use subprocess.run command using the below example. The subprocess.run will wait for the command to complete and it will return a CompletedProcess instance. If you dont care output you can use pass check=True and it will raise subprocess.CalledProcessError exception if the command returns error code !=0.

import subprocess
class CheckProcess:

    def execute_cmd(self, cmd):
        result = False
        try:
            output = subprocess.run(cmd, shell=True, check=True)
            result = True
        except subprocess.CalledProcessError as exc:
            print('Exception hit: %s', exc)
        else:
            return result

    def check_package_installed(self, pkg):
       cmd = " ".join(["dnf list installed", pkg])
       result = self.execute_cmd(cmd)
       if result != True:
          raise Exception("Package {} not installed.".format(pkg))

c = CheckProcess()
c.check_package_installed('cchfhfjgj')

Output

Exception hit: %s Command 'dnf list installed cchfhfjgj' returned non-zero exit status 127.

---------------------------------------------------------------------------

Exception                                 Traceback (most recent call last)

<ipython-input-2-029eeeb6eef7> in <module>()
     19 
     20 c = CheckProcess()
---> 21 c.check_package_installed('cchfhfjgj')

<ipython-input-2-029eeeb6eef7> in check_package_installed(self, pkg)
     16        rv = self.subprocess(cmd)
     17        if rv != True:
---> 18           raise Exception("Package {} not installed.".format(pkg))
     19 
     20 c = CheckProcess()

Exception: Package cchfhfjgj not installed.

Please read the subprocess.run documentation specific to Python version that you are using on your system.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文