8
$\begingroup$

I am an author of a Mathematica application and I'd like to add a feature where it would check the web whether an update for the application is available. This update check should be done upon initialising it, and in as unintrusive way as possible.

That is, the update check should be carried out in a way that has no risk of producing error messages (for example if there is no internet connection), and minimizes the time it takes to initialise the package.

Is this possible? And, if so, how?

$\endgroup$
  • $\begingroup$ What I'd suggest is upon starting the application you automatically check the URL prior to loading your package. Presumably you will have a list of filenames accessible from the URL and from that you can determine the most current version. If a more recent version exists then either download and install or show a message asking what to do. Use Quiet to suppress errors but also check the internet connection option value prior to attempting a connection. $\endgroup$ – Mike Honeychurch Oct 25 '16 at 7:01
  • $\begingroup$ @MikeHoneychurch This is quite helpful. Could you suggest some Mathematica functions that may be helpful to get me started? $\endgroup$ – QuantumDot Oct 25 '16 at 15:14
  • $\begingroup$ It is a bit difficult without seeing the code for your application but if it is in e.g. DynamicModule, the in the Initialization I would start with an Import or URLFetch or similar to retrieve the current list of available packages. I would include a date string in the package name and maybe snip that bit off and convert to AbsoluteTime and sort. So now you have you current package date you compare to the existing package date (AbsoluteTime). If the one on the server is more recent then retrieve it with e.g. Import or Get and so on $\endgroup$ – Mike Honeychurch Oct 26 '16 at 6:54
  • 1
    $\begingroup$ @QuantumDot Have a look at the FormTracer package by Cyrol et al. (arXiv:1610.09331) The package comes with an automatic updater that checks the Version variable of PacletInfo.m in the GitHub repo and alerts the user if an update is available. I think that should be a good start for your own implementation. $\endgroup$ – vsht Nov 2 '16 at 16:10
8
$\begingroup$

I haven't added this to a package in a long-long time (since SEUploader), so keep in mind that the following answer is not based on practical experience.

The following answer is about checking for updates only, not about in-place automatic updating. It is specifically about an update check that is run automatically, and not triggered by a user.

First let's think about what may go wrong?

The check may produce errors. But we can Quiet them, so it's okay.

The check may be slow. Now this is difficult because I believe that there's nothing you can do to prevent this. Even if you check for common problems, such as no internet connections, network communication could always take a long time. You could set a short timeout, but a timeout that's short enough not to delay loading is probably too short for a reliable check.

I suggest that the only good to check is asynchronously.

The new-in-11 function URLSubmit seems to make this easy. (For older versions use URLFetchAsynchronous). It can do the communication asynchronously and run a function when it has finished. Example:

URLSubmit[..., HandlerFunctions -> <|"BodyReceived" -> (Print[#Body] &)|>]

It is important that the handler should be as simple as possible, should not block, and should run as fast as possible, because it will be run as a preemptive evaluation. There is a chance that it will interrupt another evaluation. Make sure that any symbols you use in the handler function are in your package's private context, and don't do anything that may interfere with any already running evaluation that got stopped in the middle.


When designing your update check mechanism, try to do it in such a way that your users won't be at high risk if your server is compromised. In particular, avoid ToExpression, as this can run arbitrary code.


Here's an example. This assumes that there's an URL from where we can retrieve the version in a form such as 0.1.2.

(* this function shows a new version notification *)
notify[message_] := 
 If[$Notebooks, MessageDialog[message], Print[message]]

(* is it a version string? *)
versionQ[s_] := 
 StringQ[s] && StringMatchQ[StringTrim[s], (DigitCharacter | ".") ..]

(* parse version string to version list *)
parseVersion[s_] := FromDigits /@ StringSplit[StringTrim[s], "."]

$latestVersionURL = "http://myserver.org/mypackage/vers";
$myVersionList = parseVersion["0.1.2"]; (* local package version *)

(* handler function for URLSubmit *)
handler[asc_] :=
 Quiet@Module[{body, latestVersionList},
   body = asc["Body"];
   If[Not@MatchQ[body, {_?versionQ, ___}],
    Return[]
    ];
   latestVersionList = parseVersion@First[body];
   If[Not@OrderedQ[{latestVersionList, $myVersionList}],
    notify["New version available for package Q: " <> First[body]]
    ];
   ]

(* We use Quiet in case user has $AllowInternet = False or a network error arises *)
Quiet@URLSubmit[
 $latestVersionURL,
 HandlerFunctions -> <|"BodyReceived" -> handler|>
]

Other things to consider:

  • What if in the future you want to change the way the version information is returned by your server? Write your update check code in a way that old versions of the package won't misbehave when they receive a new-format version or no version at all.

  • Constant update notifications are annoying. You may want to give your users the option to turn them off. To do this, your package must implement some sort of persistent storage. How to do this well is a whole another topic, but generally the information should be saved in $UserBaseDirectory/ApplicationData/YourPackage. Make sure that it is saved and read in a way that there won't be trouble if in a future version you change the storage format. Consider what would happen if your users upgrade from a very old package version to the latest, which has changed the persistent storage mechanism.

    It is possible that LocalSymbol can help with this but I haven't used it, and I know that it has problems when your home directory has spaces in its path (as is very commonly the case on Windows).

$\endgroup$
  • $\begingroup$ Can we use the service Kernel for such a check (instead of the master Kernel)? $\endgroup$ – Alexey Popkov Nov 6 '16 at 16:08
  • $\begingroup$ Very nice! Could we use a Quiet[Check[(*handler stuff*), Null]] construction to escape immediately at the first sign of trouble? $\endgroup$ – QuantumDot Nov 6 '16 at 16:34
  • $\begingroup$ @QuantumDot Check doesn't escape. It runs to the end. E.g. try Check[1/0; 0^0, Null], it shows both messages. Escape must be manual: when you detect trouble, you Return[] or Throw[]. If you Throw[], be sure to use a private tag. $\endgroup$ – Szabolcs Nov 6 '16 at 17:39

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.