Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global options give "ignored" warning if packages are listed multiple times #1619

Open
josephwright opened this issue Jan 12, 2025 · 26 comments · Fixed by #1625
Open

Global options give "ignored" warning if packages are listed multiple times #1619

josephwright opened this issue Jan 12, 2025 · 26 comments · Fixed by #1625
Assignees
Labels
bug category base (latex) fixed in dev Fixed in development branch, not in stable release

Comments

@josephwright
Copy link
Member

Brief outline of the bug

As in the title, if a package takes a load-time option and this is given globally, loading the package two or more times results in a warning.

Minimal example showing the bug

\RequirePackage{latexbug}       % <--should be always the first line (see CONTRIBUTING)!
\begin{filecontents*}[overwrite]{testpackage.sty}
\ProvidesExplPackage{testpackage}{2025-02-04}{}{}
\keys_define:nn { testpackage }
  {
    testoption .code:n = { \def \testmacro { seen } } ,
    testoption .usage:n = load
  }
\ProcessKeyOptions [ testpackage ]
\endinput
\end{filecontents*}
\documentclass[testoption]{article}

\usepackage{testpackage}
\usepackage{testpackage}
\end{document}

Log file (required) and possibly PDF file

test.log

@josephwright
Copy link
Member Author

First attempt at a fix in #1606 was not successful - plan B required!

@josephwright
Copy link
Member Author

My original idea was simply to skip options that show up in the global list when setting up the scope warning. However, that fails as we need to allow for keyvals both in the global option and the package loading. Presumably this means we actually need to install new code for the option and to test each time whether to issue a warning.

@josephwright
Copy link
Member Author

I have take the points from the older PR and have multiple test files now :)

@u-fischer
Copy link
Member

Couldn't one simply test if the option has already been set at a previous load with this value?

So that this here e.g. doesn't warn either

\usepackage[testoption=bar]{package}
\usepackage[testoption=bar]{package}

I mean the main point of the load warning is to notify users that they perhaps didn't get the expected option. To ignore a simple repeat shouldn't harm (or could be an info message). A few stray warnings, if user use a option in different ways, like testoption and later testoption=true are imho acceptable.

@davidcarlisle
Copy link
Member

basically agree with @u-fischer here, I think the "global option" part is just one case, the main thing is not to warn if you call the same option again, whether that's from an an explicit package option or via the global option list

@Skillmon
Copy link
Contributor

For the global option: A second loading of an already loaded package should ignore the classoptions. They have already been given and parsed on first load, it doesn't make any sense to reprocess them.

@davidcarlisle
Copy link
Member

For the global option: A second loading of an already loaded package should ignore the classoptions. They have already been given and parsed on first load, it doesn't make any sense to reprocess them.

hmm that seems an easily documentable (and implementable) mechanism that would solve the global options problem, although if the repeat load issue that Ulrike gives above is solved, then that may mean that the global options are not a problem anyway so wouldn't need this special (non) handling.

@u-fischer
Copy link
Member

For the global option: A second loading of an already loaded package should ignore the classoptions. They have already been given and parsed on first load, it doesn't make any sense to reprocess them.

I'm not sure that is makes no difference. For load options yes, but is that true for options, that are reprocessed on the second load? So do you get here red or green?:

\documentclass[packagecolor=red]{article}
\usepackage[packagecolor=green]{package}
\usepackage{package}

(but that is more an academical question, I don't think that such option appear often as global options ...)

@FrankMittelbach
Copy link
Member

The interesting question would be what should happen if the first load of "package" happens inside the class, then

\documentclass[packagecolor=red]{class}
\usepackage{package}

The users might reasonably expect that loading "package" in the preamble" leads to applying the global option so color becomes red.

On the other other hand, documenting that global options are never reapplied is understandable too, in which case to overwrite what happened in the class, the user would need to go

\usepackage[packagecolor=green]{package}

in the preamble. So he or she would need to understand what is loaded by the package (but a preamble that works with one class with global options would lead to different results to a different class with the same global options).

On the whole I lean towards reprocessing as that seems easier to understand, so in Ulrike's and my example the result would be "red".

@josephwright
Copy link
Member Author

For the global option: A second loading of an already loaded package should ignore the classoptions. They have already been given and parsed on first load, it doesn't make any sense to reprocess them.

That was more or less plan 'A' :)

@josephwright
Copy link
Member Author

josephwright commented Jan 13, 2025

The idea of having options work 'load only' was that the kernel can't know if

\usepackage[packagecolor=green]{package}
\usepackage[packagecolor=red]{package}

makes any sense at all. So the idea was that packagecolor=green 'wins', and we warn that the second call has been ignored. We don't error here as for example

\usepackage[packageopt]{package}
\usepackage[packageopt]{package}

is likely to be OK. But I'd not allowed for global options applying to a package load with no options at all.

It's a little awkward, but we could adjust so that

\usepackage[packagecolor=green]{package}
\SetKeys[package]{packagecolor=blue}
\usepackage[packagecolor=red]{package}

only does anything 'interesting' for the \SetKeys line - this would need us to modify the key code for packagecolor here so it does a check if it's being called by \ProcessKeyOptions or by \SetKeys. (A change here would require us to wrap the internal key implementation in a suitable conditional.)

If we want to be sure of what options are given in a keyval sense, I think we have to track these: I'm not sure we could use a string comparison as there’s brace-stripping, normalisation etc., quite apart for the question of different load lines with different keyvals - what about

\documentclass[load-opt-1 = bong]{article}
\usepackage[load-opt-1 = {foo}]{package}
\usepackage[load-opt-1 = foo, load-opt-2 = {bar}]{package}
\usepackage[load-opt-2 = bar]{package}

which is why my feeling remains to disable load options the first time a package is loaded - it's then just a question of what messages (if any) should be issued for (a) global options (b) package options on a second or subsequent load.

@Skillmon
Copy link
Contributor

For the global option: A second loading of an already loaded package should ignore the classoptions. They have already been given and parsed on first load, it doesn't make any sense to reprocess them.

I'm not sure that is makes no difference. For load options yes, but is that true for options, that are reprocessed on the second load? So do you get here red or green?:

\documentclass[packagecolor=red]{article}
\usepackage[packagecolor=green]{package}
\usepackage{package}

(but that is more an academical question, I don't think that such option appear often as global options ...)

I guess we can construct cases in which both are sensible or nonsense from an author's point of view. For instance what if the global options contain packagecolor=red (provided that multiple packages and/or the class use this key), then I explicitly load that package in my preamble with packagecolor=green, and load another package afterwards that requires this package (and doesn't use any options). From a user's point of view I'd be very confused that it turns out red.

My opinion stands, the class options should only be applied once to each package, any later loading of the package that uses the same keys should overwrite these options, even if the package is included more than once and the later calls don't have any option. That's the only behaviour that gives consistent results and is documentable. I already spent some time on this problem and that's why expkv-opt ended up with exactly this behaviour.

Another point: Is .usage:n = load meant as only the first load time, or any loading in the preamble? My understanding was that those are first-time load options only.

@josephwright
Copy link
Member Author

My opinion stands, the class options should only be applied once to each package, any later loading of the package that uses the same keys should overwrite these options, even if the package is included more than once and the later calls don't have any option.

I'm not sure I follow - could you add an example?

@josephwright
Copy link
Member Author

For the global option: A second loading of an already loaded package should ignore the classoptions. They have already been given and parsed on first load, it doesn't make any sense to reprocess them.

I'm not sure that is makes no difference. For load options yes, but is that true for options, that are reprocessed on the second load? So do you get here red or green?:

\documentclass[packagecolor=red]{article}
\usepackage[packagecolor=green]{package}
\usepackage{package}

(but that is more an academical question, I don't think that such option appear often as global options ...)

I guess we can construct cases in which both are sensible or nonsense from an author's point of view. For instance what if the global options contain packagecolor=red (provided that multiple packages and/or the class use this key), then I explicitly load that package in my preamble with packagecolor=green, and load another package afterwards that requires this package (and doesn't use any options). From a user's point of view I'd be very confused that it turns out red.

My opinion stands, the class options should only be applied once to each package, any later loading of the package that uses the same keys should overwrite these options, even if the package is included more than once and the later calls don't have any option. That's the only behaviour that gives consistent results and is documentable. I already spent some time on this problem and that's why expkv-opt ended up with exactly this behaviour.

Another point: Is .usage:n = load meant as only the first load time, or any loading in the preamble? My understanding was that those are first-time load options only.

My idea (which may not be shared by others) was that as a package can only actually be loaded once, the option would be applicable only during that first load, then skipped (with or without a warning) if the package was 'called' again with options.

@u-fischer
Copy link
Member

Another point: Is .usage:n = load meant as only the first load time, or any loading in the preamble? My understanding was that those are first-time load options only.

load clearly means only the first time a package is loaded, any loading in the preamble is usage:n=preamble?

My opinion stands, the class options should only be applied once to each package,

yes, I think I agree. Everything else is confusing.

@josephwright
Copy link
Member Author

load clearly means only the first time a package is loaded, any loading in the preamble is usage:n=preamble?

preamble is any setting before \begin{document}, so \usepackage[..]{foo} or \SetKeys[foo]{...} in the preamble.

@davidcarlisle
Copy link
Member

I think one consistent and easily documented view is that all later calls are equivalent to

\SetKeys[package]{options from usepackage}

so the original example

\usepackage{testpackage}
\usepackage{testpackage}

would generate no warning but

\usepackage[testoption]testpackage}
\usepackage[testoption]{testpackage}

is

\usepackage[testoption]{testpackage}
\SetKeys[testpackage]{testoption}

which gives an error that a load option is used.

It's possible to try to track usage and allow "harmless" repeats but I'm not sure the result is really understandable (just as the classic unused option warnings got increasingly confusing as more packages used options with values.)

@Skillmon
Copy link
Contributor

Skillmon commented Jan 14, 2025

I'm not sure I follow - could you add an example?

Basically what @davidcarlisle describes (in a maybe even more convoluted manner):

\documentclass[pkg-option=foo]{article}

\usepackage[pkg-option=bar]{package}

\usepackage{otherpackage} % does \RequirePackage{package} inside of it

It would be very strange to expect this to behave like pkg-option=foo.

The next question is: Does \PassOptionsToPackage win over class options? I think currently it does (not tested, just from recollection of the code), and I think this is the correct way.

@josephwright
Copy link
Member Author

@davidcarlisle, @Skillmon But that's what we already have :) The issue comes up becuase global options apply to every \usepackage, even when it has no explicit options set

@car222222
Copy link
Contributor

So the question is whether this should apply even when a subsequent \usepackage command wishes to change a "global option".

Why is there a problem with allowing such changes? Is it because the change would be global, rather than applying only "within this package"?

@dbitouze
Copy link
Contributor

The issue comes up because global options apply to every \usepackage, even when it has no explicit options set

This is a handy feature for document language: specifying it at the \documentclass level means we don't have to do it for every language-sensitive package (\babel, \varioref, etc.).

@josephwright
Copy link
Member Author

So the question is whether this should apply even when a subsequent \usepackage command wishes to change a "global option".

Why is there a problem with allowing such changes? Is it because the change would be global, rather than applying only "within this package"?

The original idea was that the kernel can't really know what all keyval options do. So if a package author says one is 'load only' they've decided it only makes sense once, when the package is loaded. I imagine this could be for example as the package processes options early then takes one of two code paths in loading, so that once the decision is made, there's no going back.

@josephwright
Copy link
Member Author

\documentclass[pkg-option=foo]{article}

\usepackage[pkg-option=bar]{package}

Should end up passing pkg-option=foo,pkg-option=bar to the package, which is then down to the internals of how the keys are defined by the package to decide what happens. I'll double-check, but I think that is already what happens - the usage:n = load stuff only applies after the first time a package is loaded (or rather after the first use of \ProcessKeyOptions).

@davidcarlisle
Copy link
Member

@davidcarlisle, @Skillmon But that's what we already have :) The issue comes up becuase global options apply to every \usepackage, even when it has no explicit options set

The global options are available in the code but they are not added to the package options unless you add them. The suggestion is that usepackage should know if the package is already loaded that it is a kv package and if so, acts exactly like SetKeys, using the explicit options and not using the global options at all. So the example at the top with two calls with no options the first handles the global options, the second is just SetKeys with an empty list and does nothing

@josephwright
Copy link
Member Author

The global options are available in the code but they are not added to the package options unless you add them. The suggestion is that usepackage should know if the package is already loaded that it is a kv package and if so, acts exactly like SetKeys, using the explicit options and not using the global options at all. So the example at the top with two calls with no options the first handles the global options, the second is just SetKeys with an empty list and does nothing

Ah, right - updated PR shortly

@josephwright josephwright linked a pull request Jan 15, 2025 that will close this issue
7 tasks
@josephwright josephwright changed the title Global options give "ignored" warning if packages are listed multipl times Global options give "ignored" warning if packages are listed multiple times Jan 15, 2025
@Skillmon
Copy link
Contributor

Skillmon commented Jan 15, 2025

@davidcarlisle, @Skillmon But that's what we already have :) The issue comes up becuase global options apply to every \usepackage, even when it has no explicit options set

Not entirely sure what you're referring to, but I guess this statement and the related behaviour David and I argue for:

I think one consistent and easily documented view is that all later calls are equivalent to

\SetKeys[package]{options from usepackage}

No, we only have this behaviour for load options (which might throw warnings), not for any other .usage:n, making the current state of the system highly inconsistent.

@github-project-automation github-project-automation bot moved this from Pool (unscheduled issues) to Done in upcoming LaTeX2e releases Jan 15, 2025
@josephwright josephwright added the fixed in dev Fixed in development branch, not in stable release label Jan 15, 2025
@josephwright josephwright reopened this Jan 15, 2025
@github-project-automation github-project-automation bot moved this from Done to In progress in upcoming LaTeX2e releases Jan 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug category base (latex) fixed in dev Fixed in development branch, not in stable release
Projects
Status: In progress
Development

Successfully merging a pull request may close this issue.

7 participants