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

欢迎!有关如何使用本网站的信息,请参阅关于页面。

0
工具 CLI

我有一个程序,它接受多个通过命令行界面传递的选项,并且我还允许这些选项在配置文件中指定。这样,我们不必在所有调用中都传递--quiet,我们还可以在.splint.edn中将映射{quiet true}放入其中,以便自动启用。有时 coverting~ (覆盖配置文件) 传递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}。这将让我可以选择适当的值。

非常感谢!

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

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

如果配置文件由命令选项设置,或者帮助信息必须声明地面实况默认值而不仅仅是配置文件设置,则程序可以在单独的映射中声明默认值,并从中计算 CLI 定义和实际选项设置:通过一个将默认值打包进 CLI 定义选项描述的功能,从默认值映射中计算 CLI 定义,并通过按顺序合并默认值映射、配置设置和 CLI 设置来计算有效选项设置。
我不确定我完全理解你所描述的代码路径。我在已接受的答案下做了更多详细说明,因为 `:default-fn`(在某种程度上有所帮助)对我的工作很有用。如果你愿意分享,我很乐意了解更多关于你的想法。这听起来很有趣,只是把抽象转换为实际代码有些困难。

1 个答案

+1
 
最佳答案

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

抱歉这有点复杂。也许我可以简单解释一下。

我的程序可以向stdout打印内容。为了检查它是否应该这样做,它会查看上下文对象中的`:quiet`。`:quiet`的值确定逻辑如下

如果用户传递了`--quiet`,则`:quiet`为true。
否则如果用户已在配置文件中设置了`quiet`,则`:quiet`为true。
否则`:quiet`为false。

我希望建议从工具的`--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值首先应用于选项。有时候你可能想在
;; 解析完成后再应用默认值,或者专门根据
;; map中其他选项值来计算默认值。对于这些
;; 情况,你可以使用:default-fn来指定一个函数,在解析完成后
;; 对于任何没有值的选项进行调用,并且
;; 将完成的、解析后的选项map作为一个单独的参数传递。
;; :default-fn (constantly 42)实际上等同于:default 42,除非
;; 你有一个非幂等选项(使用:update-fn或:assoc-fn)-- 在这种
;; 的情况下,任何:default值将作为初始选项值而不是nil使用,
;; 并且:default-fn将被调用来计算最终的选项值,如果命令行上
;; 没有给出(因此,:default-fn可以覆盖:default)

我即将添加

;; 注意::default-fn的结果不执行验证(这是一个
;; 需要讨论的开放性问题,并不被认为是当前的错误)。
by
我阅读了它,但我发现它相当密集。将这段内容与readme中上面多个示例中演示的规范个别部分的多个示例进行比较。

我将尝试阅读一些更易读的内容。
...