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

欢迎!请参阅关于页面了解更多这个网站的工作方式。

0
tools.cli

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

然而,如果在 tools.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}。这让我可以选择合适的价值。

非常感谢!

您可以通过几种方式实现,而无需更改 tools.cli 的任何内容。

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

如果配置文件由命令选项设置,或者帮助信息必须说明真实默认值而不是配置文件设置,则程序可以在单独的映射中声明默认值,并从其中计算 CLI 定义和有效选项设置:使用将默认值捆绑到 CLI 定义选项描述中的功能,从默认值映射中计算 CLI 定义,并通过合并默认值映射、配置设置和 CLI 设置来计算有效选项设置,按此顺序。
我不确定我是否完全理解你描述的代码路径。我在接受答案下的评论中进行了更详细的说明,因为 `:default-fn`(在某种程度上)对我来说会起作用。如果你愿意分享你的想法,我会很高兴。这个想法似乎很有趣,但将抽象转为实际代码很难。

1 个回答

+1

被选中
 
最佳答案

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

对不起,有点混淆。也许我可以简单解释一下。

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

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

我希望在 tools.cli 的 `--help` 概述中显示 `false` 默认值,因为这看起来很漂亮(如下所示)。

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

然而,通过在 `cli-options` 中添加 `:default false`,`cli/parse-opts` 会无条件返回 `{:options {:quiet false}}`,因此我无法知道用户是否提供了 `--quiet` 选项,或者是 tools.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}
```
我忘记正式提出了,所以如果你接受拉取请求,我可以编写一个或两个示例。
贡献库不接受 PR。我会为这个创建一个 JIRA,如果你已签署 CLA,你可以提供一个补丁。

如果你不想那么正式,在 Slack 上向我提出一些关于文档更改的建议,我会根据那些建议更新 README。
by
顺便说一句,README里已经写了

;; :default值首先应用于选项。有时你可能希望在解析完成后应用默认值,或者
;; 特别地,根据映射中的其他选项值来计算默认值。在这种情况下,你可以使用
;; :default-fn来指定一个函数,该函数在解析完成后没有值的任何选项上被调用,并且
;; 它接收一个完整的、解析过的选项映射作为唯一的参数。
;; :default-fn (constantly 42)实际上与 :default 42相同,除非
;; 你有一个非幂等选项(有 :update-fn 或 :assoc-fn) -- 在这种情况下,任何
;; :default值都用作初始选项值而不是nil,并且如果命令行上没有提供
;; (因此,:default-fn可以覆盖:default),则会调用:default-fn来计算最后的选项值。
;; :default-fn (constantly 42)实际上等效于:default 42,除非
;; 你有非幂等(:update-fn 或 :assoc-fn)的选项 -- 在这种情况下,任何
;; :default值都用作初始选项值而不是nil,并且如果命令行上没有提供

(因此,:default-fn可以覆盖 :default)

和我将要增加
;; 注意:对 :default-fn 的结果不会进行验证(这是一个开放的问题,目前
;; 被视为讨论话题,而不是错误)。
by

我读了这个,但我觉得它相当密。将那段话与readme中上面多个示例演示的规格的各个部分进行比较。
...