15

For writing a package it's usually important to be able to define package options. Since the package I'm writing is in expl3 I wondered whether there's a l3ish way of defining key-value-package-options.

I know there's l3keys as part of expl3, but as far as I can tell, it does not evaluate options given to the package directly, but has to be called using a wrapper macro (can that be done automatically?).

Looking at the source code of fontspec it seems to utilize standard LaTeX2e's option processing facilities. Concluding from that I would use kvoptions for key-value-interfaces even for l3, although it creates l2 constructions such as \newifs.

On the other hand there's siunitx which uses the (poorly documented) package l3keys2e (@DavidCarlisle I do not want to reverse engineer) and the l3keys syntax for kv-option-processing. But is that a safe or recommended way? In my package I intend to use l3keys anyway.

To visualize what I want (a prototypical nonsense-MWE):

\documentclass{article}
\usepackage[
    quack,% should evaluate to quack=true
    font=sffamily,% should set a font token list to sffamily
    logo=false,% should set a boolean to false
    size=3% should set an integer
]{mypack}
\begin{document}
    \quackfig
\end{document}

with mypack.sty:

\RequirePackage{expl3,xparse}
\ProvidesExplPackage{mypack}{2017-05-26}{v1.0}{Quack}

\RequirePackage{graphicx}

\bool_new:N \l__mypack_optkeys_quack_bool
\bool_set_true:N \l__mypack_optkeys_quack_bool
\tl_new:N \l__mypack_optkeys_font_tl
\bool_new:N \l__mypack_optkeys_logo_bool
\int_new:N \l__mypack_optkeys_size_int
\int_set:Nn \l__mypack_optkeys_size_int {10}

% Process options here
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}
\ProcessOptions\relax

\NewDocumentCommand{\quackfig}{}{
    \begin{figure}[htb]
    \bool_if:NT \l__mypack_optkeys_quack_bool {
        \tl_if_eq:nnT {\l__mypack_optkeys_font} {sffamily} {\sffamily}
        Quack!
    }
    \bool_if:NT \l__mypack_optkeys_logo_bool {
        \includegraphics[height=\l__mypack_optkeys_size_int cm]{example-image}
    }
    \end{figure}
}

QUESTION: What is the recommended way for kv-option-processing with l3 and how would mypack.sty look then?

  • Could you elaborate on the lack of documentation in l3keys2e, perhaps on LaTeX-L or by direct mail? – Joseph Wright May 26 '17 at 7:54
  • @JosephWright Currently not, I'll try my best to remember when I have got some more free time. The main point however is that for me it's pretty unclear, which options specifically are processed by \ProcessKeysPackageOptions and \ProcessKeysOptions and whether there's a way to hide some options from those commands (only to set with a wrapper). But probably that's another question. – TeXnician May 26 '17 at 7:57
  • 2
    Side remark: I would avoid to declare to many package options. A setup \usepackage{myquack}\myquacksetup{...} is much more flexible and avoids option clash errors if the package is loaded more than once. (It also looks odd that you are passing an option to a class, but this perhaps only meant as example). – Ulrike Fischer May 26 '17 at 7:59
  • @UlrikeFischer Without that passing the test document would not compile for me (that's absolutely not intended in the real example). Concerning the package options I would at least like to have the flexibility (and know how to do it, as I know with l2e). Whether that will be applied is open by now. – TeXnician May 26 '17 at 8:01
12

Irrespective of the key processor you are using, making use of keyval options for packages has two distinct parts:

  • Defining the keys
  • Processing the LaTeX2e package option list using the keyval parser

What is crucial to understand here is that keyval package options are simply keys which have been defined at the point that the option list is processed. (LaTeX2e has no built-in keyval support for packages, so there is no concept of an internal list of keys.)

When using expl3 and specifically l3keys to define options, the package l3keys2e is the easiest approach to make keys available as options. This is done by defining the keys then processing the options

\RequirePackage{expl3,l3keys2e}
\ExplSyntaxOn
\keys_define:nn { mypkg }
  { pkgopt .tl_set:N = \l__mypkg_pkgopt_tl }
\ProcessKeysOptions { mypkg } % Parses the option list
\keys_define:nn { mypkg }
  { notpkgopt .tl_set:N = \l__mypkg_notopt_tl }

The above will define one keyval option for the package: pkgopt, which will store the given value in the \l__mypkg_pkgopt_tl. The second key notpkgopt is defined after keyval processing so is not available to \ProcessKeysOptions: it is therefore not a package option.

If one wishes to define all keys 'up front' but only make some available as options, the usual approach would be to use multiple paths, say mypkg / pkgopts and mypkg / otheropts.

One common use for this approach is to disable an option which is strictly load-time only

\RequirePackage{expl3,l3keys2e}
\ExplSyntaxOn
\keys_define:nn { mypkg }
  { pkgopt .tl_set:N = \l__mypkg_pkgopt_tl }
\ProcessKeysOptions { mypkg } % Parses the option list
\keys_define:nn { mypkg }
  { 
     pkgopt .code:n = 
      \msg_error:nnn { mypkg } { load-time-only } { pkgopt } 
  }

As noted in a comment, it is usually best to favour a setup command (\mypkgsetup) which can be used after loading, unless the options directly affect package loading set up, etc.


l3keys2e provides two commands, \ProcessKeysOptions and \ProcessKeysPackageOptions. These exist as there are different ways one might wish to handle global ('class') options. With \ProcessKeysOptions the global options are included in the parsing, but will not raise an error if they are not defined. This allows for things like draft or french as global options: you might support say draft but not any language options. On the other hand, \ProcessKeysPackageOptions does not look at the global list at all: it only parses what was given to \usepackage (or passed using \PassOptionsToPackage, etc.).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.