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

欢迎!请参阅 关于 页面,了解更多关于了这个如何运作的信息。

0
tools.cli

我有一个程序,它可以从 CLI 接收一组选项,我将它们也允许以配置文件的形式指定。这样,就可以将 --quiet 传递给所有调用,而不是在每个调用中都传递。可以在 .splint.edn 中放入映射 {quiet true},这样就可以自动启用它。有时通过在 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选项可能更适合你?

by
很抱歉这可能会造成困惑。也许我可以更简单一点来解释它。

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

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

我希望在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}
```
by
我忘记正式提出了,所以如果你接受pull请求,我可以写一两个例子。
by
贡献库不接受PR。我会创建一个JIRA,如果你已经签署了CLA,你可以提供补丁。

如果您不想那么正式,请通过Slack向我提出一些关于建议文档更改的建议,我会根据这些意见更新readme文件 :)
无论如何,README中已说明:

;; :default的值首先应用于选项。有时您可能想
;; 在解析完成后应用默认值,或者具体到
;; 基于映射中其他选项值来计算默认值。在这些
;; 的情况下,您可以使用:default-fn来指定一个函数,该函数在解析完成后
;; 适用于任何未设置的选项,并且
;; 将解析完成的完整选项映射作为其单个参数传递。
;; :default-fn (constantly 42) 与 :default 42基本相同,除非
;; 选项是非幂等的(带有:update-fn或:assoc-fn)--在这种情况下,
;;任意的:default值将用作初始选项值而不是空值,
;; 并且:default-fn将在命令行未提供任何值时调用以计算最终
;; 选项值(因此,:default-fn可以覆盖:default)

我即将添加:

;; 注意:验证不会对:default-fn的结果进行
;;(这是一个开放的问题,目前不认为这是一个错误)
我读了这个,但我觉得它相当密集。将此段与README中上面演示的规格的各个部分的多个示例进行比较。

我将尝试使内容更加易读。
...