2024 State of Clojure 问卷调查中分享您的看法!

欢迎!有关本站工作原理的更多信息,请参阅关于页面。

0
tools.cli

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

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

我选择在工具的 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}。这将使我能够根据需要选择正确的值。

非常感谢!

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

如果配置文件是众所周知的话,程序可以首先读取配置文件并使用其内容作为 CLI 默认值。(技术上,配置文件将是默认文件。)

如果配置文件由命令选项设置,或者帮助信息必须声明基本默认值而不是配置文件设置,则程序可以在不同的映射中声明默认值,并从中计算 CLI 定义和实际选项设置:使用将默认值捆绑到 CLI 定义选项描述的功能来从默认映射计算 CLI 定义,然后按照以下顺序合并默认映射、配置设置和 CLI 设置来计算实际选项设置。
我不确定我完全理解您描述的代码路径。我在接受答案下的评论中更详细地说明了情况,因为 `:default-fn`(在得到一些帮助后)将对我有用。如果您愿意分享,我很乐意了解更多关于您的想法,这听起来很有趣,但将抽象转化为实际代码有点困难。

1 个回答

+1

选择
 
最佳答案

我不太确定您想做什么,但我认为 `:default-fn` 选项可能更适合您?

抱歉有些令人困惑。也许我能用更简单的方式解释一下。

我的程序可以将内容打印到标准输出。为了检查是否应该这样做,它会查看上下文对象中的`:quiet`。确定`:quiet`的值遵循以下逻辑:

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

我希望建议在工具-cli的`--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}
```
我忘了正式提出,所以如果你接受pull请求,我可以写一个示例或两个。
贡献库不接受PR。我会为此创建一个JIRA,如果你已经签署了CLA,你就可以为它提供一个补丁。

如果你不希望那么正式,请在Slack上给我一些建议的文档更改,我会根据那些建议更新readme :)
顺便说一下,README文档已经说了

;; :default值首先应用于选项。有时候你可能想要
;; 在解析完成之后应用默认值,或者特别指定
;; 根据映射中的其他选项值来计算默认值。在这种情况下,
;; 你可以使用:default-fn来指定一个函数,用于解析完成后
;; 没有值的任何选项,并且将完整的、解析后的选项映射
;; 作为唯一的参数传递。
;; :default-fn (constantly 42) 等效于 :default 42
;; 除非你有一个非幂等选项(带有:update-fn或:assoc-fn)
;; 在这种情况下,任何:default值都将用作初始选项值而不是nil,
;; 并且如果命令行中没有提供值,:default-fn将被调用以
;; 计算最终选项值(因此,:default-fn可以覆盖:default)

我将添加以下内容

;; 注意:不执行对:default-fn结果的验证(这是一个
;; 开放讨论的问题,目前不认为是错误)。
我读到了那部分内容,但我觉得相当难以理解。把那一段与readme中的多个示例做比较,这些例子在上面展示了上述规格的具体部分。

我尝试编写更易于理解的内容。
...