应用程序配置

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接口中的每个方法都是抽象的,但在抽象类中,我们也只能使用抽象方法   初始化Java中声明的、未初始化的变量会发生什么情况?   java BouncyCastle openPGP将字节[]数组加密为csv文件   在Java中将类A(和所有子类)映射到类B的实例的字典   RSA公钥编码,在Java和Android中,代码相同,结果不同   java在安卓中实现数字检测语音识别   java取消选择复选框   java如何在其他配置中重用Maven配置XML片段   java有没有一种有效的方法来检查HashMap是否包含映射到相同值的键?   spring处理程序调度失败;嵌套的例外是java。lang.NoClassDefFoundError:org/apache/http/client/HttpClient   带有ehcache的java多层缓存   java如何访问chromium(或任何其他浏览器)cookie   java通过将两个集合与spring data mongodb data中的条件合并来获取计数   安卓中R.java的语法错误