argparse在Python类中的验证

huangapple go评论114阅读模式
英文:

argparse validation in a python class

问题

我尝试使用面向对象的方法来处理我的Python代码,最终将其转换为由PyInstaller创建的.EXE文件。这个想法是从用户输入中传递一系列参数给程序,最终会变成类似于(myprogram.exe -secureFolder C:/Users -thisisacsvfile.csv -countyCode 01069 -utmZone 15)的形式。

我可以初始化一个类定义并传递参数,如下所示:

  1. import argparse
  2. import sys
  3. class myprogram():
  4. def __init__(self, secureFolder, inputCsvFile, countyCode, utmZone):
  5. self.secureFolder = secureFolder
  6. self.inputCsvFile = inputCsvFile
  7. self.countyCode = countyCode
  8. self.utmZone = utmZone
  9. if __name__ == '__main__':
  10. parser = argparse.ArgumentParser()
  11. parser.add_argument("secureFolder", help="A directory where the files are located", type=str)
  12. parser.add_argument("inputCsvFile", help="A CSV file containing the results for a particular county (e.g. 36107.csv)", type=str)
  13. parser.add_argument("countyCode", help="The FIPS county code", type=str)
  14. parser.add_argument("utmZone", help="The UTM zone code for that specific county (e.g. 18)", type=int)

然而,我需要验证每个用户参数,这是我感到困惑的部分。换句话说,我需要检查secureFolder是否存在,inputCsvFile是否确实是CSV文件并且包含特定列以及其他操作来验证其余的参数。我不确定这些操作应该在哪里执行?在类定义之后还是之前?

在采用面向对象的方法之前,我是这样做的:

  1. # 检查是否传递了所有参数
  2. undefined_arguments = [attr for attr in vars(args) if getattr(args, attr) is None]
  3. if undefined_arguments:
  4. print("以下参数未定义:", undefined_arguments)
  5. else:
  6. print("所有参数都已定义。")
  7. # 1a. 检查inputCsvFile
  8. if args.inputCsvFile is None:
  9. sys.exit("请选择要处理的输入CSV文件 (例如:inputCsvFile.../myfile.csv)")
  10. else:
  11. if not os.path.isfile(args.inputCsvFile):
  12. sys.exit(f"文件 {args.inputCsvFile} 似乎不存在...请检查文件是否存在或您是否具有访问权限")
  13. else:
  14. grid_file_csv = args.inputCsvFile
  15. print(f"{args.inputCsvFile} 已找到...")
  16. # 1b. 检查inputCsvFile是否为CSV文件
  17. if not args.inputCsvFile.endswith('.csv'):
  18. raise ValueError("无效的输入文件。预期是一个CSV文件。")
  19. sys.exit('未传递正确的CSV文件...')

希望这可以帮助你理解在面向对象编程方法中应该如何验证参数。如果需要进一步的帮助,请随时提问。

英文:

I'm trying an OOP approach to my Python code which eventually will be converted to an .EXE file created with PyInstaller. The idea is to pass a series of arguments from the user input (n to a program that eventually will go something like (myprogram.exe -secureFolder C:/Users -thisisacsvfile.csv -countyCode 01069 -utmZone 15).

I can initialize a class definition and pass the arguments like:

  1. import argparse
  2. import sys
  3. class myprogram():
  4. def __init__(self, secureFolder, inputCsvFile, countyCode, utmZone):
  5. self.secureFolder = secureFolder
  6. self.inputCsvFile = inputCsvFile
  7. self.countyCode = countyCode
  8. self.utmZone = utmZone
  9. if __name__ == '__main__':
  10. parser = argparse.ArgumentParser()
  11. parser.add_argument("secureFolder", help = "A directory where the files are located", type=str)
  12. parser.add_argument("inputCsvFile", help="A CSV file containing the results for a particular county (e.g. 36107.csv)", type=str)
  13. parser.add_argument("countyCode", help = "The FIPS county code", type = str)
  14. parser.add_argument("utmZone", help = "The UTM zone code for that specific county (e.g. 18)", type = int)

However, I need to validate every user argument and that's the part where I'm getting confused. In other words, I need to check if the secureFolder exists, if the inputCsvFile is indeed a CSV and contains some specific columns and other operations for the rest of the arguments. What I don't know exactly, where do I perform these operations? After the class definition?
Before the OOP approach, I was doing something like:

  1. # Check if all the arguments were passed
  2. undefined_arguments = [attr for attr in vars(args) if getattr(args, attr) is None]
  3. if undefined_arguments:
  4. print("The following arguments were not defined:", undefined_arguments)
  5. else:
  6. print("All arguments were defined.")
  7. # 1a. Check inputCsvFile
  8. if args.inputCsvFile is None:
  9. sys.exit("Please select an input CSV file to process (e.g. inputCsvFile.../myfile.csv) ")
  10. else:
  11. if not os.path.isfile(args.inputCsvFile):
  12. sys.exit (f"File {args.inputCsvFile} doesn't appear to exists...please check if the file exists or if you have privileges to access it")
  13. else:
  14. grid_file_csv = args.inputCsvFile
  15. print (f"{args.inputCsvFile} found...")
  16. # 1b. Check if inputCsvFile is a CSV:
  17. if not args.inputCsvFile.endswith('.csv'):
  18. raise ValueError("Invalid input file. Expected a CSV file.")
  19. sys.exit('No propper CSV file has been passed...')
  20. # 2. Check if the FIPS code
  21. if args.countyCode is None:
  22. sys.exit("Please specify a valid county code (e.g. -countyCode3607)")
  23. # Check the UTM area code
  24. if args.utmzone is None:
  25. sys.exit("Please specify a valid UTM zone area (e.g. -utmZone 16): ")
  26. if args.utmZone is not None:
  27. val = args.utmZone
  28. if val < 1 and val > 20:
  29. raise Exception('UTM zone area should be between 1 and 20')
  30. sys.exit()

答案1

得分: 1

这主要是一个风格和偏好的问题。在我看来,尽可能在构建任何类之前都应该验证构建该类所需的值 - 尤其是当它依赖于用户输入时。

所以,获取命令行参数,验证它们,然后构建你的类。类似于这样:

  1. import argparse
  2. from sys import stderr
  3. import os
  4. class myprogram():
  5. def __init__(self, secureFolder, inputCsvFile, countyCode, utmZone):
  6. self.secureFolder = secureFolder
  7. self.inputCsvFile = inputCsvFile
  8. self.countyCode = countyCode
  9. self.utmZone = utmZone
  10. def __str__(self):
  11. return f'{self.secureFolder=}, {self.inputCsvFile=}, {self.countyCode=}, {self.utmZone=}'
  12. @staticmethod
  13. def validate(ns):
  14. if not os.path.isdir(ns.secureFolder):
  15. print(f'{ns.secureFolder} is not a valid folder', file=stderr)
  16. return None
  17. try:
  18. with open(ns.inputCsvFile) as _:
  19. ...
  20. except Exception as e:
  21. print(f'Unable to open {ns.inputCsvFile} due to {e}', file=stderr)
  22. return None
  23. if ns.countyCode is None:
  24. print(f'{ns.countyCode} is an invalid county code', file=stderr)
  25. return None
  26. if (z := ns.utmZone) is None or z < 1 or z > 60:
  27. print(f'{z} is not a valid utmZone', file=stderr)
  28. return None
  29. return myprogram(ns.secureFolder, ns.inputCsvFile, ns.countyCode, ns.utmZone)
  30. if __name__ == '__main__':
  31. parser = argparse.ArgumentParser()
  32. cli = [
  33. ('-secureFolder', 'A directory where the files are located', str),
  34. ('-inputCsvFile', 'A CSV file containing the results for a particular county (e.g., 36107.csv)', str),
  35. ('-countyCode', 'The FIPS county code', str),
  36. ('-utmZone', 'The UTM zone code for that specific county (e.g., 18)', int)
  37. ]
  38. for c, h, t in cli:
  39. parser.add_argument(c, help=h, type=t)
  40. args = parser.parse_args()
  41. if (mp := myprogram.validate(args)) is None:
  42. print('Unable to construct class instance')
  43. else:
  44. # 此时我们有一个有效的myprogram类实例(mp)
  45. print(mp)

希望这对你有所帮助。

英文:

This is largely a question of style and preference. In my view, wherever possible, values needed to construct any class should be validated before the class is constructed - especially when it relies on user input.

So, get the command line arguments, validate them, then construct your class. Something like this:

  1. import argparse
  2. from sys import stderr
  3. import os
  4. class myprogram():
  5. def __init__(self, secureFolder, inputCsvFile, countyCode, utmZone):
  6. self.secureFolder = secureFolder
  7. self.inputCsvFile = inputCsvFile
  8. self.countyCode = countyCode
  9. self.utmZone = utmZone
  10. def __str__(self):
  11. return f&#39;{self.secureFolder=}, {self.inputCsvFile=}, {self.countyCode=}, {self.utmZone=}&#39;
  12. @staticmethod
  13. def validate(ns):
  14. if not os.path.isdir(ns.secureFolder):
  15. print(f&#39;{ns.secureFolder} is not a valid folder&#39;, file=stderr)
  16. return None
  17. try:
  18. with open(ns.inputCsvFile) as _:
  19. ...
  20. except Exception as e:
  21. print(f&#39;Unable to open {ns.inputCsvFile} due to {e}&#39;, file=stderr)
  22. return None
  23. if ns.countyCode is None:
  24. print(f&#39;{ns.countyCode} is an invalid county code&#39;, file=stderr)
  25. return None
  26. if (z := ns.utmZone) is None or z &lt; 1 or z &gt; 60:
  27. print(f&#39;{z} is not a valid utmZone&#39;, file=stderr)
  28. return None
  29. return myprogram(ns.secureFolder, ns.inputCsvFile, ns.countyCode, ns.utmZone)
  30. if __name__ == &#39;__main__&#39;:
  31. parser = argparse.ArgumentParser()
  32. cli = [
  33. (&#39;-secureFolder&#39;, &#39;A directory where the files are located&#39;, str),
  34. (&#39;-inputCsvFile&#39;, &#39;A CSV file containing the results for a particular county (e.g., 36107.csv)&#39;, str),
  35. (&#39;-countyCode&#39;, &#39;The FIPS county code&#39;, str),
  36. (&#39;-utmZone&#39;, &#39;The UTM zone code for that specific county (e.g., 18)&#39;, int)
  37. ]
  38. for c, h, t in cli:
  39. parser.add_argument(c, help=h, type=t)
  40. args = parser.parse_args()
  41. if (mp := myprogram.validate(args)) is None:
  42. print(&#39;Unable to construct class instance&#39;)
  43. else:
  44. # at this point we have a valid myprogram class instance (mp)
  45. print(mp)

答案2

得分: 0

argparse库可以为您执行一些验证操作,例如确保参数是必需的。对于其余部分,您可以编写小而简洁的验证函数:

  1. import argparse
  2. import pathlib
  3. class MyProgram:
  4. def __init__(self, secureFolder, inputCsvFile, countyCode, utmZone):
  5. self.secureFolder = secureFolder
  6. self.inputCsvFile = inputCsvFile
  7. self.countyCode = countyCode
  8. self.utmZone = utmZone
  9. def validate_dir(value):
  10. path = pathlib.Path(value)
  11. if not path.is_dir():
  12. raise ValueError()
  13. return value
  14. def validate_csv(value):
  15. path = pathlib.Path(value)
  16. if not path.exists():
  17. raise ValueError()
  18. if path.suffix != ".csv":
  19. raise ValueError()
  20. return value
  21. def validate_utm_zone(value):
  22. value = int(value)
  23. if not (1 <= value <= 20):
  24. raise ValueError()
  25. return value
  26. if __name__ == "__main__":
  27. parser = argparse.ArgumentParser()
  28. parser.add_argument(
  29. "-secureFolder",
  30. help="文件所在的目录",
  31. type=validate_dir,
  32. required=True,
  33. )
  34. parser.add_argument(
  35. "-inputCsvFile",
  36. help="包含特定县的结果的CSV文件",
  37. type=validate_csv,
  38. required=True,
  39. )
  40. parser.add_argument("-countyCode", help="FIPS县代码", required=True)
  41. parser.add_argument(
  42. "-utmZone",
  43. help="特定县的UTM区域代码(例如,18)",
  44. type=validate_utm_zone,
  45. required=True,
  46. )
  47. options = parser.parse_args()
  48. print(options)
  49. my_program = MyProgram(
  50. secureFolder=options.secureFolder,
  51. inputCsvFile=options.inputCsvFile,
  52. countyCode=options.countyCode,
  53. utmZone=options.utmZone,
  54. )

以下是一些示例运行:

  1. # 正常情况
  2. $ python3 main.py -secureFolder ./myfolder -inputCsvFile input.csv -countyCode code-blah -utmZone 15
  3. Namespace(secureFolder='./myfolder', inputCsvFile='input.csv', countyCode='code-blah', utmZone=15)
  4. # 没有提供任何参数
  5. $ python3 main.py
  6. # 目录/foobar不存在
  7. $ python3 main.py -secureFolder /foobar -inputCsvFile input.csv -countyCode code-blah -utmZone 15
  8. # 无效的utmZone
  9. $ python3 main.py -secureFolder ./myfolder -inputCsvFile input.csv -countyCode code-blah -utmZone foo
  10. # utmZone不在范围内
  11. $ python3 main.py -secureFolder ./myfolder -inputCsvFile input.csv -countyCode code-blah -utmZone 21
  12. # CSV文件不以.csv结尾
  13. $ python3 main.py -secureFolder ./myfolder -inputCsvFile main.py -countyCode code-blah -utmZone 15
  14. # csv文件不存在
  15. $ python3 main.py -secureFolder ./myfolder -inputCsvFile input.csv -countyCode code-blah -utmZone 15
  16. # 获取帮助
  17. $ python3 main.py -h
英文:

The argparse library can do some validations for you, such as ensuring that the argument is required. For the rest, you can write small, concise validation functions:

  1. import argparse
  2. import pathlib
  3. class MyProgram:
  4. def __init__(self, secureFolder, inputCsvFile, countyCode, utmZone):
  5. self.secureFolder = secureFolder
  6. self.inputCsvFile = inputCsvFile
  7. self.countyCode = countyCode
  8. self.utmZone = utmZone
  9. def validate_dir(value):
  10. path = pathlib.Path(value)
  11. if not path.is_dir():
  12. raise ValueError()
  13. return value
  14. def validate_csv(value):
  15. path = pathlib.Path(value)
  16. if not path.exists():
  17. raise ValueError()
  18. if path.suffix != &quot;.csv&quot;:
  19. raise ValueError()
  20. return value
  21. def validate_utm_zone(value):
  22. value = int(value)
  23. if not (1 &lt;= value &lt;= 20):
  24. raise ValueError()
  25. return value
  26. if __name__ == &quot;__main__&quot;:
  27. parser = argparse.ArgumentParser()
  28. parser.add_argument(
  29. &quot;-secureFolder&quot;,
  30. help=&quot;A directory where the files are located&quot;,
  31. type=validate_dir,
  32. required=True,
  33. )
  34. parser.add_argument(
  35. &quot;-inputCsvFile&quot;,
  36. help=&quot;A CSV file containing the results for a particular county&quot;,
  37. type=validate_csv,
  38. required=True,
  39. )
  40. parser.add_argument(&quot;-countyCode&quot;, help=&quot;The FIPS county code&quot;, required=True)
  41. parser.add_argument(
  42. &quot;-utmZone&quot;,
  43. help=&quot;The UTM zone code for that specific county (e.g. 18)&quot;,
  44. type=validate_utm_zone,
  45. required=True,
  46. )
  47. options = parser.parse_args()
  48. print(options)
  49. my_program = MyProgram(
  50. secureFolder=options.secureFolder,
  51. inputCsvFile=options.inputCsvFile,
  52. countyCode=options.countyCode,
  53. utmZone=options.utmZone,
  54. )

Here are some sample runs

  1. # Happy path
  2. $ python3 main.py -secureFolder ./myfolder -inputCsvFile input.csv -countyCode code-blah -utmZone 15
  3. Namespace(secureFolder=&#39;./myfolder&#39;, inputCsvFile=&#39;input.csv&#39;, countyCode=&#39;code-blah&#39;, utmZone=15)
  4. # None of the arguments supplied
  5. $ python3 main.py
  6. usage: main.py [-h] -secureFolder SECUREFOLDER -inputCsvFile INPUTCSVFILE -countyCode COUNTYCODE -utmZone UTMZONE
  7. main.py: error: the following arguments are required: -secureFolder, -inputCsvFile, -countyCode, -utmZone
  8. # Directory /foobar does not exist
  9. $ python3 main.py -secureFolder /foobar -inputCsvFile input.csv -countyCode code-blah -utmZone 15
  10. usage: main.py [-h] -secureFolder SECUREFOLDER -inputCsvFile INPUTCSVFILE -countyCode COUNTYCODE -utmZone UTMZONE
  11. main.py: error: argument -secureFolder: invalid validate_dir value: &#39;/foobar&#39;
  12. # Invalid utmZone
  13. $ python3 main.py -secureFolder ./myfolder -inputCsvFile input.csv -countyCode code-blah -utmZone foo
  14. usage: main.py [-h] -secureFolder SECUREFOLDER -inputCsvFile INPUTCSVFILE -countyCode COUNTYCODE -utmZone UTMZONE
  15. main.py: error: argument -utmZone: invalid validate_utm_zone value: &#39;foo&#39;
  16. # utmZone not in range
  17. $ python3 main.py -secureFolder ./myfolder -inputCsvFile input.csv -countyCode code-blah -utmZone 21
  18. usage: main.py [-h] -secureFolder SECUREFOLDER -inputCsvFile INPUTCSVFILE -countyCode COUNTYCODE -utmZone UTMZONE
  19. main.py: error: argument -utmZone: invalid validate_utm_zone value: &#39;21&#39;
  20. # CSV file does not end with .csv
  21. $ python3 main.py -secureFolder ./myfolder -inputCsvFile main.py -countyCode code-blah -utmZone 15
  22. usage: main.py [-h] -secureFolder SECUREFOLDER -inputCsvFile INPUTCSVFILE -countyCode COUNTYCODE -utmZone UTMZONE
  23. main.py: error: argument -inputCsvFile: invalid validate_csv value: &#39;main.py&#39;
  24. # csv does not exist
  25. $ python3 main.py -secureFolder ./myfolder -inputCsvFile input.csv -countyCode code-blah -utmZone 15
  26. usage: main.py [-h] -secureFolder SECUREFOLDER -inputCsvFile INPUTCSVFILE -countyCode COUNTYCODE -utmZone UTMZONE
  27. main.py: error: argument -inputCsvFile: invalid validate_csv value: &#39;input.csv&#39;
  28. # Get help
  29. $ python3 main.py -h
  30. usage: main.py [-h] -secureFolder SECUREFOLDER -inputCsvFile INPUTCSVFILE -countyCode COUNTYCODE -utmZone UTMZONE
  31. options:
  32. -h, --help show this help message and exit
  33. -secureFolder SECUREFOLDER
  34. A directory where the files are located
  35. -inputCsvFile INPUTCSVFILE
  36. A CSV file containing the results for a particular county
  37. -countyCode COUNTYCODE
  38. The FIPS county code
  39. -utmZone UTMZONE The UTM zone code for that specific county (e.g. 18)

huangapple
  • 本文由 发表于 2023年7月3日 16:46:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/76603188.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定