5

This is in the category of "things that work, but make me nervous."

I use a very complicated, custom document class with LuaLaTeX. But my question can easily be illustrated with this generic MWE:

\documentclass{article}
\RequirePackage{xifthen}
\RequirePackage{letltxmacro}
\LetLtxMacro\myusepackage\usepackage\relax
\LetLtxMacro\myRequirePackage\RequirePackage\relax
\renewcommand\usepackage[2][]{%
  \ifthenelse{\equal{#2}{hyperref}}{}{\myusepackage[#1]{#2}}% 
}
\renewcommand\RequirePackage[2][]{%
  \ifthenelse{\equal{#2}{hyperref}}{}{\myRequirePackage[#1]{#2}}% 
}
%
\usepackage{xstring} % should work
\usepackage{hyperref} % should do nothing
%
\begin{document}
OK
\end{document}

If the user attempts to load hyperref, it will silently be ignored. I realize that there are alternative such as \PassOptionsToPackage, but that is not the effect I seek.

I routinely patch commands from various LaTeX packages, but those are high-level commands in optional packages, rather than something as fundamental as \usepackage or \RequirePackage. So my question is: Does the above MWE code have any hidden traps?

I only chose hyperref for purposes of the MWE, so there's no need to ask why I would want to block it in a real document.

EDIT: For those who wonder why I would do this: My custom document class requires certain things to be loaded in precise order, due to \immediate\write commands. If the user loads a package prematurely, currently I detect that by throwing an error via \@ifpackageloaded in the class code, just before my own code loads the package \AtEndPreamble. But it seems to me that it would be more user-friendly to simply ignore the premature loading.

EDIT2: Based on comments and answers, I believe that I should retain my current method, rather than doing what the question asks. "Bad user interface" (per DC) is an important point.

Without getting into the over-200K bytes of my custom document class, I can illustrate what I have been doing. This has been in good working order for many months. General concept of "myscustomclass.cls" is this:

a) When mycustomclass loads, it also loads and pre-configures numerous packages. It also provides its own commands.

b) It contains \AtEndPreamble that will load and configure additional packages. These cannot be loaded until after the user's preamble, since it needs to look at the user's preamble and decide what to do.

c) At least one of the later-loaded packages has \immediate\write, which cannot occur earlier than a certain time. That is why it is deferred until \AtEndPreamble.

Currently (good working code) I do this kind of thing:

\AtEndPreamble{
  % lots of code here
  @ifpackageloaded{hyperref}{ % Should not have been loaded by user! Too early!
    \ClassError{mycustomclass}%
    {Cannot load hyperref in preamble}{See myscustomclass docs section XX.XXX}
  }{
    \usepackage[various options]{hyperref}
  }
  % lots of code here
}
  • 2
    \expandafter\def\csname ver@hyperref.sty\endcsname{3000/12/31} seems a better choice for ignoring the loading of a package. See tex.stackexchange.com/a/85700/4427 for an example of what can go wrong. – egreg Jan 28 '18 at 0:59
  • 5
    silently disabling the package loading seems a very confusing interface for the user, if they load hyperref and use a command such as \href they will just get an undefined command error with no hint of why. Better if your class went \AtBeginDocument{\@ifpackageloaded{hyperref}{\ClassError{myclass}{I told you not to load hyperref}{}}{}} so the user gets a more relevant message. – David Carlisle Jan 28 '18 at 1:05
  • 1
    The user could not load hyperref, but then they'll get an error when the next line is \hypersetup or more mysterious failures with cleveref. Or they'll get an error if the next line loads bookmark. The same goes for lots of other packages. People often load them and then do config in the preamble. The fact that the config commands will be loaded at the end of the preamble doesn't help. Any package which loads a package you drop will also cause trouble, as it will try to execute commands not yet defined. This isn't just about hyperref. – cfr Jan 28 '18 at 2:10
  • 1
    Your current approach seems good to me. I'd comment the stray line endings, but I am not sure whether it matters here or not. That may just be habit. – cfr Jan 28 '18 at 2:17
  • 1
    Freaking templates. – Johannes_B Jan 28 '18 at 4:13
8

Not sure what the question is here, but patching \usepackage as shown won't work as intended in cases such as

\usepackage{longtable,hyperref}

as it will not see hyperref as the argument, and it will completely break valid uses such as

\usepackage{hyperref}[2001/01/01]

as it removes the optional argument so latex will try to typeset [2001/01/01] in the preamble.

  • Ah, that actually answers my question, at least partially. I can parse the \usepackage mandatory argument for specific strings, and that would address the first issue. But I had not thought of the following optional argument. So indeed, there is a trap. More thought required on my part. Glad I asked. – user139954 Jan 28 '18 at 1:19
4

In addition to David's answer, consider

\usepackage{bookmark}

bookmark.sty includes

\RequirePackage{hyperref}[2010/06/18]

and a bunch of other things and then a bunch of code which depends on hyperref.

Or

\usepackage[<options>]{hyperref}

You simply discard the user's options.

Or

\usepackage{hyperref}
\usepackage{cleveref}

or some other package which must be loaded after hyperref.

Or

\usepackage{hyperref}
\hypersetup{...}
  • Indeed. But the user could not use` bookmark` anyway. This is PDF/X, where active content is forbidden. Package hyperref is only loaded because it does some encoding processing. No actual links allowed. In the case you raised, that would require an error. – user139954 Jan 28 '18 at 1:41
  • 1
    @RobtAll It is just an example. Any package which provides macros which might be used in the preamble will cause trouble. Any package which another package may load and then use will cause trouble. In any case, saying \usepackage{bookmark} will give a weird and mysterious error, whereas you could have produced a helpful and intelligible one when you found hyperref had been loaded already. You are making it more difficult for the user to figure out what the problem is rather than making it easier or, failing that, at least no harder. – cfr Jan 28 '18 at 2:14
  • 1
    Yes, I see that now. My current working code is good enough. Bad idea to "fix" it. – user139954 Jan 28 '18 at 2:17
  • @RobtAll If you want to prevent hyperlinks, perhaps it would simpler to load nohyperref instead. This will provide the hyperref interface but prevent the formation of links, and will also prevent the user from loading hyperref as the two packages conflict. (However, I'm not sure if nohyperref does the encoding processing you require, but you might want to investigate it.) – Nicola Talbot Jan 28 '18 at 15:23
  • @NicolaTalbot It is more complicated than hyperlinks. My MWE was "too minimal" in that it did not discuss the entire situation. – user139954 Jan 28 '18 at 16:21

Your Answer

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