Python "template" 命令行脚本和导入模块的模块
Python "template" module for both command line scripts and imported module
我想编写一个 python "template" 模块,以便为我的所有脚本提供相同的行为。
行为如下:
- 如果脚本在 命令行 中运行,它接受用 argparse 处理的参数。这些论点基本上是:
- 从标准输入、文件或字符串参数中获取输入 a
json
;
- 在标准输出或文件中输入
json
。
- 如果脚本作为模块导入,它会classes/functions管理以下情况:
- 从调用它的人那里输入一个对象;
- 在输出中提供一个对象,以便调用它的人可以使用它。
我做了什么:
"template"部分template.py
由于这些建议,它在命令行中的行为完全符合我的要求:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json,sys,argparse,os
def main():
parser = argparse.ArgumentParser(description='Template for python script managing JSON as input/output format. \
A JSON file can be [], {}, "string", 123, true, false, null.')
infile=['-i','--input-file']
kwinfile={'type':argparse.FileType('r'), 'help':'Input file name containing a valid JSON. Default and priority: standard input.'}
jstring=['-j','--json']
kwjstring={'type':str, 'nargs':'?', 'help':'Input file name containing a valid JSON. Default and priority: standard input.'}
outfile=['-o','--output-file']
kwoutfile={'type':argparse.FileType('w'), 'help':'Output file name. Default: standard output.', 'default':sys.stdout}
pretty=['-p','--pretty']
kwpretty={'action':'store_true', 'help':'If set, JSON output will be formatted in pretty print.'}
group = parser.add_mutually_exclusive_group()
group.add_argument(*infile, **kwinfile)
group.add_argument(*jstring, **kwjstring)
parser.add_argument(*outfile, **kwoutfile)
parser.add_argument(*pretty, **kwpretty)
args = parser.parse_args()
return(args)
def input(*data):
args=main()
# if data :
# datain=data[0]
# else :
if not sys.stdin.isatty(): # pipe
data=sys.stdin.read()
else: # no pipe
if not len(sys.argv) > 1 or (args.input_file == None and args.json == None) : # no arguments or no input
data='null'
else :
data = args.json or args.input_file.read()
try:
datain = json.loads(data)
except:
output({'script_name':(sys.argv[0]),
'error': 'Input is not a valid JSON.',
'data': data})
sys.exit(0)
return(datain)
def output(*datain) :
args=main()
if datain :
datain=datain[0]
indent = 2 if args.pretty else None
dataout = json.dumps(datain, indent=indent, ensure_ascii=False)
args.output_file.write(dataout+os.linesep)
return(dataout)
if __name__ == "__main__":
main()
我希望这是最好的实现方式。
例子"calculate_area"
现在,如果我使用
在脚本中导入它
import template as t
def main():
inp=t.input() # {"x":8, "y":2}
out={'area' : inp['x'] * inp['y'] }
return(t.output(out))
if __name__ == "__main__":
main()
脚本按照我的意愿在命令行中运行:
$ echo '{"x":8, "y":2}' | ./calculate_area.py -p
{
"area": 16
}
"calculate_sqrt" 脚本将其作为模块进行测试
现在我想要第三个脚本将其作为模块导入。
import template as t
import calculate_area as i
import numpy as np
import json
def main():
inp=json.loads(i.main())
out={'sqrt of area' : np.sqrt(inp['area']) }
return(t.output(out))
if __name__ == "__main__":
main()
问题从这里开始:
$ echo '{"x":8, "y":2}' | ./calculate_sqrt.py -p
{
"area": 16
}
{
"sqrt of area": 4.0
}
- 为什么我得到了两个输入而不是最后一个?
此外:
- 如何避免在
json
中输入?改为:"if the module is called via import
, then the input/output will be via objects, else it will be via json
in command line"?
我在这里保存了我的代码:
https://github.com/orsa-unige/python-templates/tree/simplified-example
这是我认为好的基本脚本的大纲:
import json,sys,argparse,os
def parser(argv=None):
# if argv is None, uses the sys.argv[1:]
parser = argparse.ArgumentParser(....)
...
args = parser.parse_args(argv)
return(args)
def input(args, *data):
# if data :
# datain=data[0]
if args.input_file is not None:
# input_file might be sys.stdin (if '-')
data = args.input_file.read()
# stdin should work for < redirection
# I don't know if works for pipe
...
return(datain)
def output(args, *datain) :
if datain :
datain=datain[0]
# output_file might be stdout
....
return(dataout)
def main(args):
datain = input(args, [])
dataout = output(args, datain)
return dataout
if __name__ == "__main__":
args = parser()
main(args)
如果作为脚本调用,此 运行 只会对解析器执行一次。如果导入,则由 运行 此解析器的导入程序脚本决定。
A parser
可能 运行 多次,但通常不需要 - 至少如果 Namespace
可以传递则不需要。但是每次调用解析器都会打开 input/output 个文件。由于一个文件以写入模式打开,因此可能会导致重叠打开。
解析器可能会被测试:
args = parser(['-i', 'inputfile.py', ....]
另一个脚本可以做到
from template import parser, input, output
def main(args):
... input
# do its own thing
... output
# etc
我想编写一个 python "template" 模块,以便为我的所有脚本提供相同的行为。
行为如下:
- 如果脚本在 命令行 中运行,它接受用 argparse 处理的参数。这些论点基本上是:
- 从标准输入、文件或字符串参数中获取输入 a
json
; - 在标准输出或文件中输入
json
。
- 从标准输入、文件或字符串参数中获取输入 a
- 如果脚本作为模块导入,它会classes/functions管理以下情况:
- 从调用它的人那里输入一个对象;
- 在输出中提供一个对象,以便调用它的人可以使用它。
我做了什么:
"template"部分template.py
由于这些建议,它在命令行中的行为完全符合我的要求:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json,sys,argparse,os
def main():
parser = argparse.ArgumentParser(description='Template for python script managing JSON as input/output format. \
A JSON file can be [], {}, "string", 123, true, false, null.')
infile=['-i','--input-file']
kwinfile={'type':argparse.FileType('r'), 'help':'Input file name containing a valid JSON. Default and priority: standard input.'}
jstring=['-j','--json']
kwjstring={'type':str, 'nargs':'?', 'help':'Input file name containing a valid JSON. Default and priority: standard input.'}
outfile=['-o','--output-file']
kwoutfile={'type':argparse.FileType('w'), 'help':'Output file name. Default: standard output.', 'default':sys.stdout}
pretty=['-p','--pretty']
kwpretty={'action':'store_true', 'help':'If set, JSON output will be formatted in pretty print.'}
group = parser.add_mutually_exclusive_group()
group.add_argument(*infile, **kwinfile)
group.add_argument(*jstring, **kwjstring)
parser.add_argument(*outfile, **kwoutfile)
parser.add_argument(*pretty, **kwpretty)
args = parser.parse_args()
return(args)
def input(*data):
args=main()
# if data :
# datain=data[0]
# else :
if not sys.stdin.isatty(): # pipe
data=sys.stdin.read()
else: # no pipe
if not len(sys.argv) > 1 or (args.input_file == None and args.json == None) : # no arguments or no input
data='null'
else :
data = args.json or args.input_file.read()
try:
datain = json.loads(data)
except:
output({'script_name':(sys.argv[0]),
'error': 'Input is not a valid JSON.',
'data': data})
sys.exit(0)
return(datain)
def output(*datain) :
args=main()
if datain :
datain=datain[0]
indent = 2 if args.pretty else None
dataout = json.dumps(datain, indent=indent, ensure_ascii=False)
args.output_file.write(dataout+os.linesep)
return(dataout)
if __name__ == "__main__":
main()
我希望这是最好的实现方式。
例子"calculate_area"
现在,如果我使用
在脚本中导入它import template as t
def main():
inp=t.input() # {"x":8, "y":2}
out={'area' : inp['x'] * inp['y'] }
return(t.output(out))
if __name__ == "__main__":
main()
脚本按照我的意愿在命令行中运行:
$ echo '{"x":8, "y":2}' | ./calculate_area.py -p
{
"area": 16
}
"calculate_sqrt" 脚本将其作为模块进行测试
现在我想要第三个脚本将其作为模块导入。
import template as t
import calculate_area as i
import numpy as np
import json
def main():
inp=json.loads(i.main())
out={'sqrt of area' : np.sqrt(inp['area']) }
return(t.output(out))
if __name__ == "__main__":
main()
问题从这里开始:
$ echo '{"x":8, "y":2}' | ./calculate_sqrt.py -p
{
"area": 16
}
{
"sqrt of area": 4.0
}
- 为什么我得到了两个输入而不是最后一个?
此外:
- 如何避免在
json
中输入?改为:"if the module is called viaimport
, then the input/output will be via objects, else it will be viajson
in command line"?
我在这里保存了我的代码: https://github.com/orsa-unige/python-templates/tree/simplified-example
这是我认为好的基本脚本的大纲:
import json,sys,argparse,os
def parser(argv=None):
# if argv is None, uses the sys.argv[1:]
parser = argparse.ArgumentParser(....)
...
args = parser.parse_args(argv)
return(args)
def input(args, *data):
# if data :
# datain=data[0]
if args.input_file is not None:
# input_file might be sys.stdin (if '-')
data = args.input_file.read()
# stdin should work for < redirection
# I don't know if works for pipe
...
return(datain)
def output(args, *datain) :
if datain :
datain=datain[0]
# output_file might be stdout
....
return(dataout)
def main(args):
datain = input(args, [])
dataout = output(args, datain)
return dataout
if __name__ == "__main__":
args = parser()
main(args)
如果作为脚本调用,此 运行 只会对解析器执行一次。如果导入,则由 运行 此解析器的导入程序脚本决定。
A parser
可能 运行 多次,但通常不需要 - 至少如果 Namespace
可以传递则不需要。但是每次调用解析器都会打开 input/output 个文件。由于一个文件以写入模式打开,因此可能会导致重叠打开。
解析器可能会被测试:
args = parser(['-i', 'inputfile.py', ....]
另一个脚本可以做到
from template import parser, input, output
def main(args):
... input
# do its own thing
... output
# etc