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

欢迎!请参阅关于页面,了解更多关于如何使用本站的信息。

0
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" "Run splint in parallel. Defaults to 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`为假。

我希望在工具的`--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 requests,我可以写一个或两个示例。
贡献库不接受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上的样本中展示了规范的部分。

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