应用程序配置

devapps的Python项目详细描述


Devapps-DevOps就绪应用程序

Build StatusCoverage StatusPyPI versionCode style: black

[目录]

devapp:命令行函数配置程序和运行程序

如果您有这样的设置:

importoperatorclassCalc:"""Calculator Demo"""oper_func='add'# One (PY2 compatible) way to provide type hints (optional, for less imperative code):defdo_run(self,a=int,b=int):"""Runs operator function on the arguments"""returngetattr(operator,self.oper_func,self.not_found)(a,b)defnot_found(self,*a,**kw):raiseException('not supported:',self.oper_func)

通过devapp命令,您可以在cli上提供配置(除了 配置文件和环境)并立即运行此程序:

$ devapp ./calc.py 41142

$ devapp ./calc.py b=14142

$ devapp ./calc.py oper_func=mul a=2b=100200

$ devapp ./calc.py of=mul a=2b=150300

$ exportCalc_oper_func=mul Calc_run='{"a":100}'; devapp ./calc.py  b=4400

$ devapp ./calc.py a=1b=foo # b has wrong type0.02285 [error    ] Cannot cast expected_type=int for_param=b got=foo

$ devapp ./calc.py # missing params0.02001 [error    ] Value required param=a type=int
0.02025 [error    ] Value required param=b type=int

$ devapp ./calc.py of=mul -h # help output# Calc

Calculator Demo

## Parameters| Name      | Val | F | Dflt | Descr | Expl || --------- | --- | - | ---- | ----- | ---- || oper_func | mul | C | add  |||## Actions### run

Runs operator function on the arguments

> do_run(a=<int>, b=<int>)

@app:转换类树

decorator变量允许独立运行应用程序:

#!/usr/bin/env pythonimportoperatorfromdevapps.appimportapp# one way of usage is via this decorator@app# apply the decorator to your app holding classclassCalc:"""Calculator Demo"""oper_func='add'defdo_run(calc,a=int,b=int):"""Runs operator function on the arguments"""returngetattr(operator,calc.oper_func,calc.not_found)(a,b)defnot_found(calc,*a,**kw):raiseException('not supported:',calc.oper_func)

我们

  • 添加了hashbang并使文件可执行。
  • 装饰了一流的班级。

我们现在可以直接从cli运行该程序:

$ ./calc1.py a=2b=46

$ ./calc1.py b=2a=4# mappable so supported6

$ ./calc1.py 24.1 # positionally given, rounded6

$ ./calc1.py a=24.1 # mapping found6

$ ./calc1.py oper_func=mul a=2b=48

$ ./calc1.py of=mul a=2b=4# short form for oper_func8

更多操作

类可能有更多的动作函数(默认情况下前缀为do_)。 do_run只是默认的操作-如果没有配置其他操作,则运行。

让我们添加另一个:

#!/usr/bin/env pythonimportoperatorfromdevapps.appimportapp# one way of usage is via this decorator@app# apply the decorator to your app holding classclassCalc:"""Calculator Demo"""oper_func='add'op=lambdacalc:getattr(operator,calc.oper_func,calc.not_found)defdo_run(calc,a=int,b=int):"""Runs operator function on the arguments"""returncalc.op()(a,b)defdo_list_sum(calc,args=[0]):"""Sums up all numbers given"""returnsum(args)defnot_found(calc,*a,**kw):raiseException('not supported:',calc.oper_func)

运行它:

$ ./calc2.py list_sum "1,2,3"6

$ ./calc2.py ls "1, 2, 3"# short form for function supported6

帮助输出

-h提供标记格式的帮助输出:

$ ./calc2.py -h

# Calc

Calculator Demo

## Parameters

| Name      | Val | F | Dflt | Descr | Expl |
| --------- | --- | - | ---- | ----- | ---- |
| oper_func | add |   |      |       |      |

## Actions

### list_sum

Sums up all numbers given

> do_list_sum(args=[0])

### run

Runs operator function on the arguments

> do_run(a=<int>, b=<int>)

降价?

因为这允许添加很多结构化信息——我们可以使用这些信息来很好地为输出着色、提供toc、放入readme等等。

-hc显示实现:

$ ./calc2.py -hc

# Calc

Calculator Demo

## Parameters

| Name      | Val | F | Dflt | Descr | Expl |
| --------- | --- | - | ---- | ----- | ---- |
| oper_func | add |   |      |       |      |

## Actions

### list_sum

Sums up all numbers given

``python=
def do_list_sum(calc, args=[0]):
    return sum(args)
``

### run

Runs operator function on the arguments

``python=
def do_run(calc, a=int, b=int):
    return calc.op()(a, b)
``

If the terminal width is not wide enough for the parameter tables we render the parameters vertically. -hu (classic unix) forces this always.

默认值可配置

当提供参数时,让我们检查-h输出:

$ ./calc2.py of=multiply 1 -h | head -n 10

# Calc

Calculator Demo

## Parameters

| Name      | Val      | F | Dflt | Descr | Expl |
| --------- | -------- | - | ---- | ----- | ---- |
| oper_func | multiply | C | add  |       |      |

正如您从cli中看到的,我们的价值体现在文档中。
F(from)列显示值的提交位置。

提供者

通过其他方式更改应用程序的默认值更有意义 而不是cli。

内置我们还有两个所谓的“提供者”,即配置的提供者:

0.[编程默认值] 1.配置文件 2.环境 3.cli

按给定顺序重写彼此的值。这个顺序可以改变。

文件

让我们创建一个配置文件,将默认运算符更改为mul,并将第一个函数参数的默认值更改为^{a

$python-c"if 1:cfg={'oper_func':'mul','run':{'a':10.3}}# write to user config dir:fromappdirsimportuser_config_dirasucdfromjsonimportdumpswithopen(ucd()+'/Calc.json','w')asfd:fd.write(dumps(cfg))"

现在我们可以运行应用程序,而不需要提供a并获得乘法:

$ ./calc1.py b=42420

Positionally you could overwrite a still on the CLI, so we do not map one value only to b

$ ./calc1.py 542210
$ ./calc1.py 50.03074 [error    ] Value required param=b type=int

这里是-h

的输出
$ ./calc1.py -h

# Calc

Calculator Demo

## Parameters

| Name      | Val | F | Dflt | Descr | Expl |
| --------- | --- | - | ---- | ----- | ---- |
| oper_func | mul | F | add  |       |      |

## Actions

### run

Runs operator function on the arguments
:::warning
Defaults modified (by File):
- a: 10 (was <int>)
:::

> do_run(a=10, b=<int>)

再次重新配置了应用程序-这次是由配置文件(f)重新配置的

观察int值-它是从float转换的,因为这是函数显式要求的。

Yes, we did mutate inplace the defaults of the Calc.do_run function - i.e. process wide! Keep this in mind when using that feature - reading the source code is then misleading. Help output shows modifications and origin rather prominently as you can see.

我们暂时删除文件。

环境

也支持-但必须以lit.eval格式提供结构化值:

$ exportCalc_oper_func=mul Calc_run='{"a":4.2}'; ./calc1.py b=312

By default we do NOT support short forms at the environ provider and also we are case sensitive. Note that the overridden defaults still had been casted to the original types of the function signature.

帮助输出,如预期:

$ export Calc_oper_func=xxx Calc_run='{"b":4.2}';./calc1.py a=2.1 -h

# Calc

Calculator Demo

## Parameters

| Name      | Val | F | Dflt | Descr | Expl |
| --------- | --- | - | ---- | ----- | ---- |
| oper_func | xxx | E | add  |       |      |

## Actions

### run

Runs operator function on the arguments
:::warning
Defaults modified (by Env):
- b: 4 (was <int>)
:::

> do_run(a=<int>, b=4)

到目前为止,应用程序中没有关于操作员功能允许值的指示。 这就是我们在配置应用程序时接受虚假值的原因。

嵌套功能块

当应用程序变得更复杂时,您可以递归地将功能块嵌套/组合到彼此中

#!/usr/bin/env pythonimportoperatorfromdevapps.appimportappclassLog:"""Print Logger"""level=10defdo_testmsg(log,ev='hello'):log.msg(30,ev)return''defmsg(log,level,ev,**kw):iflevel>=log.level:print('[%s] %s%s'%(level,ev,kw))@appclassCalc:"""Calculator Demo"""oper_func='add'log=Logdefdo_run(calc,a=int,b=int):"""Runs operator function on the arguments"""of=calc.oper_funccalc.log.msg(10,'Calculating',operation=of,a=a,b=b)res=getattr(operator,of,calc.not_found)(a,b)calc.log.msg(20,'Returning',result=res)returnresdefnot_found(calc,*a,**kw):raiseException('not supported:',calc.oper_func)
$ ./calc_tree.py 1299[10] Calculating {'operation': 'add', 'a': 1, 'b': 299}[20] Returning {'result': 300}300

$ ./calc_tree.py log.level=20of=mul 1003[20] Returning {'result': 300}300

$ ./calc_tree.py l.l=20of=mul 1003# shorthand notation for nested blocks[20] Returning {'result': 300}300

$ ./calc_tree.py l.t "hi there"# calling nested functions[30] hi there {}

Of course you could have defined the inner class directly within the main app class as well

帮助输出(再次使用覆盖的默认值):

$ ./calc_tree.py l.l=20 l.t.ev=hi of=mul -h

# Calc

Calculator Demo

## Parameters| Name      | Val | F | Dflt | Descr | Expl || --------- | --- | - | ---- | ----- | ---- || oper_func | mul | C | add  |||## Actions### run

Runs operator function on the arguments

> do_run(a=<int>, b=<int>)

---
## log

Print Logger

### Parameters| Name  | Val | F | Dflt | Descr | Expl || ----- | --- | - | ---- | ----- | ---- || level |20| C |10|||### Actions#### testmsg

:::warning
Defaults modified (by CLI):
- ev: hi (was hello)
:::

> do_testmsg(ev=hi)

树导航

嵌套类的排列可以在运行时导航,如下所示:

#!/usr/bin/env pythonfrom__future__importprint_function# for Python2importoperatorimportattrfromdevappsimportroot,parent,as_dictfromdevappsimportappclassInner:inner_var=1defdo_nav_demo(inner):returnroot(inner).do_run(inner.inner_var,inner.Deep.deep_var)classDeep:deep_var=2defdo_nav_demo(deep):print(root(deep).app_var,parent(deep).do_nav_demo())return''@app.appclassApp:inner=Innerapp_var=0defdo_run(app,a=1,b=2):returna,bdefdo_dump(app,asdict=False):print(appifnotasdictelseas_dict(app))return''

在配置的树上调用App.inner.Deep.do_nav_demo()

$ ./calc_tree_nav.py av=100 i.iv=200 i.D.dv=300 i.D.nd
100(200, 300)

序列化/预打印

可打印配置状态并转储dict:

$ ./calc_tree_nav.py av=1 i.D.dv=42 du # du matched to dump
App(app_var=1, inner=Inner(Deep=Inner.Deep(deep_var=42), inner_var=1))


$ ./calc_tree_nav.py app_var=2 inner.Deep.deep_var=42 dump asdict=true{'app_var': 2, 'inner': {'Deep': {'deep_var': 42}, 'inner_var': 1}}

dict格式可以像管道一样导入到配置文件中,以便后续运行。

Currently we do not serialize function parameter changes.

学分

Hynek Schlawack

测试/ci:

备选方案

已经有很多选项可以解析cli:

更棒的libs,来自argh文档:

似乎每个人都应该种一棵树,养一个儿子,然后编写一个命令行解析器。

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java用变化的替换字符串替换子字符串   从数据库中断中恢复的oracle Java DAL?   Android/Java页边距位于左/右/底部   java如何用相同的源代码构建不同的APK?(我发现了一个错误)   java正则表达式,仅当字符串以一行中的3个数字开头时才匹配第一个数字   使用以xml为输入的给定端点调用java中的rest-ful web服务?   java长字符串转换为UTF8引发异常   java如何使用截取方法获取ArrayList   java将计算列添加到可观察列表中   正则表达式如何在java正则表达式中使用组?   java正则表达式只接受字母表和空格,不允许在字符串的开头和结尾使用空格   java简单onclick按钮在安卓中不起作用   java如何在Spring中只实现Crudepository的特定方法?   java无法使用json对象NPE读取jsonarray   java我可以添加maven依赖项,这些依赖项被打包为除此之外的任何东西。罐子