2

I would like to know if I can use l3keys to process multiple submodules as pgfoption can do ?

Here is a MWE to illustrate my question:

  • a package file mypkg.sty (containing in comment the error I get when I try to compile the LaTeX file):
\ProvidesExplPackage{mypkg}{2024/01/01}{v0.0.0}{mypkg test}

\keys_define:nn { mypkg / title }
{
    title .choice:,
    title .usage:n = { general },
    title .default:n = { regular },
    title / regular .code:n = 
    {
        \PackageWarning {mypkg} {We~use~regular~title!}
    },
    title / smallcaps .code:n = 
    {
        \PackageWarning {mypkg} {We~use~smallcaps~title!}
    },
    title / unknown .code:n = { \PackageWarning {mypkg} {unknown~title~option!} },
}

\keys_define:nn { mypkg / font }
{
    font .choice:,
    font .usage:n = { general },
    font .default:n = { regular },
    font / regular .code:n = 
    {
        \PackageWarning {mypkg} {We~use~regular~font!}
    },
    font / bold .code:n = 
    {
        \PackageWarning {mypkg} {We~use~bold~font!}
    },
    font / unknown .code:n = { \PackageWarning {mypkg} {unknown~font~option!} },
}

% \ProcessKeyOptions % -> ! LaTeX Error: Unknown option 'title' for package mypkg.
\ProcessKeyOptions[mypkg/title, mypkg/font] % -> ! LaTeX Error: Unknown option 'title' for package mypkg.
% \ProcessKeyOptions[mypkg/title] % -> ! LaTeX Error: Unknown option 'font' for package mypkg.
% \ProcessKeyOptions[mypkg/font] % -> ! LaTeX Error: Unknown option 'title' for package mypkg.
  • a LaTeX file, test.tex:
\documentclass{article}
\usepackage[title=smallcaps, font=regular]{mypkg}

\begin{document}
TEST
\end{document}
2
  • Your separation of choice keys into submodules looks odd, imho. I guess this is caused by stripping down your real example for this MWE, but otherwise, just drop the submodules.
    – Skillmon
    Commented Dec 27, 2023 at 16:43
  • @Skillmon Yes it is cause by stripping down my real usage example. Otherwise, I agree that removing the submodules and keeping everything in the same module is a better option.
    – R. N
    Commented Dec 27, 2023 at 21:18

2 Answers 2

2

The following uses an unknown handler of yet another submodule to load the keys of your submodules. This breaks global keys (so the options given to the \documentclass) but otherwise works reasonable well.

\ProvidesExplPackage{mypkg}{2024/01/01}{v0.0.0}{mypkg test}

\seq_const_from_clist:Nn \c__mypkg_load_submodules_seq { title, font }
\keys_define:nn { mypkg / load }
  {
    unknown .code:n =
      \__mypkg_keymodules:Vn \l_keys_key_str {#1}
  }
\cs_new_protected:Npn \__mypkg_keymodules:nn #1#2
  {
    \seq_map_inline:Nn \c__mypkg_load_submodules_seq
      {
        \keys_if_exist:nnT { mypkg / ##1 } {#1}
          {
            % missing feature in l3keys, we have no better way to detect an
            % omitted value, so we have to guess that all empty values are in
            % fact omitted values.
            \tl_if_empty:nTF {#2}
              { \keys_set:nn { mypkg / ##1 } { #1 } }
              { \keys_set:nn { mypkg / ##1 } { #1 = {#2} } }
            \prg_break:
          }
      }
    \msg_error:nnn { mypkg } { unknown-key } {#1}
    \prg_break_point:
  }
\cs_generate_variant:Nn \__mypkg_keymodules:nn { Vn }
\msg_new:nnn { mypkg } { unknown-key } { Unknown~ key~ #1~ encountered. }

\keys_define:nn { mypkg / title }
{
    title .choice:,
    title .usage:n = { general },
    title .default:n = { regular },
    title / regular .code:n = 
    {
        \PackageWarning {mypkg} {We~use~regular~title!}
    },
    title / smallcaps .code:n = 
    {
        \PackageWarning {mypkg} {We~use~smallcaps~title!}
    },
    title / unknown .code:n = { \PackageWarning {mypkg} {unknown~title~option!} },
}

\keys_define:nn { mypkg / font }
{
    font .choice:,
    font .usage:n = { general },
    font .default:n = { regular },
    font / regular .code:n = 
    {
        \PackageWarning {mypkg} {We~use~regular~font!}
    },
    font / bold .code:n = 
    {
        \PackageWarning {mypkg} {We~use~bold~font!}
    },
    font / unknown .code:n = { \PackageWarning {mypkg} {unknown~font~option!} },
}

\ProcessKeyOptions[mypkg/load] % -> ! LaTeX Error: Unknown option 'title' for package mypkg.

PoC

This is a PoC to parse the initial options via the submodules (so that global options work), but any future options via our load-submodule acting with an unknown-key handler, all that while parsing every option just once.

This is a bit convoluted admittedly.

\ProvidesExplPackage{mypkg}{2024/01/01}{v0.0.0}{mypkg test}

\seq_const_from_clist:Nn \c__mypkg_load_submodules_seq { title, font }
\cs_new_protected:Npn \__mypkg_keymodules:nn #1#2
  {
    \seq_map_inline:Nn \c__mypkg_load_submodules_seq
      {
        \keys_if_exist:nnT { mypkg / ##1 } {#1}
          {
            % missing feature in l3keys, we have no better way to detect an
            % omitted value, so we have to guess that all empty values are in
            % fact omitted values.
            \tl_if_empty:nTF {#2}
              { \keys_set:nn { mypkg / ##1 } { #1 } }
              { \keys_set:nn { mypkg / ##1 } { #1 = {#2} } }
            \prg_break:
          }
      }
    \msg_error:nnn { mypkg } { unknown-key } {#1}
    \prg_break_point:
  }
\cs_generate_variant:Nn \__mypkg_keymodules:nn { Vn }
\msg_new:nnn { mypkg } { unknown-key } { Unknown~ key~ #1~ encountered. }

\keys_define:nn { mypkg / title }
{
    title .choice:,
    title .usage:n = { general },
    title .default:n = { regular },
    title / regular .code:n = 
    {
        \PackageWarning {mypkg} {We~use~regular~title!}
    },
    title / smallcaps .code:n = 
    {
        \PackageWarning {mypkg} {We~use~smallcaps~title!}
    },
    title / unknown .code:n = { \PackageWarning {mypkg} {unknown~title~option!} },
    unknown .code:n = {}
}

\keys_define:nn { mypkg / font }
{
    font .choice:,
    font .usage:n = { general },
    font .default:n = { regular },
    font / regular .code:n = 
    {
        \PackageWarning {mypkg} {We~use~regular~font!}
    },
    font / bold .code:n = 
    {
        \PackageWarning {mypkg} {We~use~bold~font!}
    },
    font / unknown .code:n = { \PackageWarning {mypkg} {unknown~font~option!} },
    unknown .code:n = {}
}

\ProcessKeyOptions[mypkg/title]
\ProcessKeyOptions[mypkg/font]

\keys_define:nn { mypkg / load }
  {
    unknown .code:n = {}
  }
\ProcessKeyOptions[mypkg/load] % -> ! LaTeX Error: Unknown option 'title' for package mypkg.

\keys_define:nn { mypkg / load }
  {
     unknown .code:n =
      \__mypkg_keymodules:Vn \l_keys_key_str {#1}
  }
\clist_map_inline:nn { title, font }
  {
    \keys_define:nn { mypkg / #1 }
      {
        \unknown .code:n =
          \msg_error:nne { mypkg } { unknown-key } \l_keys_key_str
      }
  }
2
  • Can you develop a bit more about the consequences of this global keys breaking ? Because it seems interesting.
    – R. N
    Commented Dec 27, 2023 at 21:29
  • @R.N that goes into the implementation details of \ProcessKeyOptions, basically for the global options it does a \keys_if_exist:nTF as well to filter which options of the global list to forward to the package, which this here breaks, as none of the options are defined in mypkg/load.
    – Skillmon
    Commented Dec 27, 2023 at 21:43
3

I'm not sure why you'd want to define keys this way. But you can simply add management of unknown keys in each module.

\ProvidesExplPackage{mypkg}{2024/01/01}{v0.0.0}{mypkg test}

\keys_define:nn { mypkg/title }
{
    title .choice:,
    title .usage:n = { general },
    title .default:n = { regular },
    title / regular .code:n = 
    {
        \PackageWarning {mypkg} {We~use~regular~title!}
    },
    title / smallcaps .code:n = 
    {
        \PackageWarning {mypkg} {We~use~smallcaps~title!}
    },
    title / unknown .code:n = { \PackageWarning {mypkg} {unknown~title~option!} },
    unknown .code:n = {},
}
\keys_define:nn { mypkg/font }
{
    font .choice:,
    font .usage:n = { general },
    font .default:n = { regular },
    font / regular .code:n = 
    {
        \PackageWarning {mypkg} {We~use~regular~font!}
    },
    font / bold .code:n = 
    {
        \PackageWarning {mypkg} {We~use~bold~font!}
    },
    font / unknown .code:n = { \PackageWarning {mypkg} {unknown~font~option!} },
    unknown .code:n = {},
}

\ProcessKeyOptions[mypkg/title]
\ProcessKeyOptions[mypkg/font]
10
  • 1
    if you do that like this you can't use the title key in a second package loading, it is then ignored: \usepackage[title=smallcaps]{mypkg}\usepackage[title=regular]{mypkg}. Imho it is better to put keys meant as package options under one label. Commented Dec 27, 2023 at 15:54
  • @UlrikeFischer I agree. But why should one want to load twice a package?
    – egreg
    Commented Dec 27, 2023 at 15:58
  • 1
    @R.N my solution works for loading a package twice (though it doesn't work well for the global list). A hybrid solution of this and mine would do.
    – Skillmon
    Commented Dec 27, 2023 at 21:43
  • 1
    @R.N as I wrote, I would use only one label and not split the keys into submodules. For package options that is imho overdoing. Commented Dec 27, 2023 at 21:55
  • 1
    @R.N no, this is possible without parsing keys multiple times, though it's a bit convoluted. See the edit in my answer.
    – Skillmon
    Commented Dec 27, 2023 at 23:34

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .