2024 年 Clojure 状态调查! 中分享您的想法。

欢迎!请参阅 关于 页面以获取更多关于如何使用本站的信息。

0
Profile picture tools.cli

我有一个程序,它在 CLI 中接受多个选项,我也允许这些选项在配置文件中指定。这样,而不是在所有调用中传递 --quiet,人们可以将 {quiet true} 放入 .splint.edn 以自动启用它。有时覆盖配置文件通过在 CLI 中传递选项很有价值。因此,在构建选项映射时,我首先加载配置文件,然后合并 CLI 选项:(merge local-config cli-options)

但是,如果我在 tools.cli 规范中指定一个默认选项,我会失去这种行为,因为默认选项会无条件地覆盖配置文件,因为它总是存在于 CLI 选项映射中。

我倾向于不在我的 tools.cli 规范中省略 :default 键,而是在选项描述字符串中只写 "默认为 true。"。例如:[nil "--[no-]parallel" "并行运行 splint。默认为 true。"] 然后稍后 (get config :parallel true) 然后再 (merge local-config cli-options)。这意味着我的摘要中没有漂亮的打印 FALSE(例如, --[no]-parallel FALSE 并行运行 splint。

除了“你已经解决了,什么也不做!”以外,我还有一些建议可以解决这个小问题。
1) 从 cli/parse-opts 返回一个额外的键,类似于::defaults。它将是一个选项的序列或集合,这些选项使用了默认值。这样,我可以在某些选项中添加逻辑,如 (merge (select-keys cli-options defaults) local-config (apply dissoc cli-options defaults))
2) 在 cli/parse-opts 规范中添加一个 :default-display,将打印默认值而不将其添加到选项映射中。这将由用户(开发者)负责确保实际上将其添加到选项映射中或以某种方式使用它。
3) 添加一些功能,允许在摘要中指定默认值但仍将其他值放入映射中。例如, --[no]-parallel FALSE,但映射得到 {:parallel :false}。这将使我能够根据需要选择正确的值。

非常感谢!

by
在不修改tools.cli的情况下,您可以通过几种方法来实现这一点。

如果配置文件是已知的,则程序可以先读取配置文件并将其内容用作CLI的默认值。(实际上配置文件将是一个默认文件。)

如果配置文件是由命令选项设置的或者帮助信息必须说明基准默认值而非配置文件设置,则程序可以在一个独立的映射中声明默认值,并从它计算CLI定义和有效选项设置:使用将默认值捆绑到CLI定义选项描述中的函数从默认值映射中计算CLI定义,并按此顺序合并默认值映射、配置设置和CLI设置以计算有效选项设置。
by
我不确定是否完全理解你描述的代码路径。我在接受答案下的评论中进行了更多解释,因为`:default-fn`(一些帮助)对我来说会起作用。如果你愿意分享你的想法,我会很高兴了解更多关于你的想法,这只是很难将抽象转换为实际代码。

1 答案

+1
by
selected by
 
最佳答案

我不太确定你是否完全理解你试图做什么,但我认为`:default-fn`选项可能会对你更有用?

by
抱歉有点混乱。也许我可以稍微简单地解释一下。

我的程序可以打印信息到标准输出。为了确定是否应该打印,它会查看上下文对象的`:quiet`。`:quiet`的值确定逻辑如下:

如果用户传入了`--quiet`,则`:quiet`为真。
否则,如果用户在配置文件中设置了`quiet`,则`:quiet`为真。
否则`:quiet`为假。

我希望在工具的`--help`摘要中显示`false`的默认值,因为它的格式非常不错(如以下所示)。

```
--[no-]quiet              false  不打印诊断信息,只显示摘要。
```

然而,通过在我的`cli-options`中添加`:default false`,`cli/parse-opts`无条件返回`{:options {:quiet false}}`,因此我无法知道是用户提供了`--quiet`选项,还是工具的`.cli`仅仅返回了默认值。

再次尝试`:default-fn`,我认为它应该行得通,但文档中没有任何使用示例,这就是我之前没有成功的原因。也许一个或两个示例会帮助未来的其他人。

下面演示了`:default-fn`如何与`:default`和传入的参数交互,供有相同要求的其他人参考

```
(def cli-options
  [[nil "--[no-]quiet" "不打印诊断信息,只显示摘要。"
    :default false
    :default-fn (fn [v] (prn :default-fn v) :new-val)]])

(defn validate-opts
  [args]
  (let [{:keys [options]} (cli/parse-opts args cli-options)]
    (prn :options options)))
```

```
$ clojure -M:run
:default-fn {:quiet false}
:options {:quiet :new-val}

$ clojure -M:run --quiet
:options {:quiet true}

$ clojure -M:run --no-quiet
:options {:quiet false}
```
by
我忘记了正式提出,所以如果你们接受拉取请求,我可以编写一个或两个示例。
by
贡献库不接受PRs。我会创建一个JIRA来处理这个问题,如果你已经签署了CLA,你可以提供这个问题的补丁。

如果你不希望过于正式,可以在Slack上向我提出一些关于文档改进的建议,我会根据这些建议更新readme文件 :)
顺便提一下,README里已经说过了

;; 默认值首先应用于选项。有时你可能希望在解析完成后再应用默认值,或
;; 考虑到其他选项值在映射中特定地计算默认值。在那些情况下,你可以使用
;; :default-fn来指定一个函数,该函数在解析完成后对于任何未指定值的选项被调用,
;; 并且传给该函数作为单一参数的完整、解析后的选项映射。
;; :default-fn (constantly 42) 在没有非幂等选项(具有:update-fn或:assoc-fn)的情况下相当于
;; :default 42 - 在这种情况下,任何:default值都用作初始选项值而不是nil,并且
;; 如果命令行上没有给出值,则:default-fn将被调用以计算最终的选项值(因此,:default-fn可以
;; 覆盖:default)
;; and I'm about to add
;; 注意::default-fn的结果不接受验证(这是一个待讨论的开放问题,并且目前
;; 不被认为是bug)。

我即将添加

;; 注意::default-fn的结果不接受验证(这是一个待讨论的开放问题,并且目前
;; 不被认为是bug)。
我看了那个,但我觉得它相当密集。将那个段落与readme中上面多个示例中各个部分的多个示例进行比较。

我将尝试一个更易读的东西。
...