16

How can I implement persistent storage for packages/applications? For example, how can my package have settings which persist across sessions?


I have implemented this more than once in the past. This question is to share our solutions and keep them for future reference. This the kind of application where reliability is paramount because it is often used in packages that may be distributed to many people.

Things to pay attention to:

  • Compatibility with different Mathematica versions. This is a typical requirement for packages. Solutions must be compatible with 10.0 and all later versions. Earlier version compatibility is even better, but I'm not asking for it because associations are too convenient ...

  • Persistent storage is useful for package settings. Solutions presented here shouldn't stand in the way of changing the settings format in future versions of the package (adding or removing settings, changing the format, etc.)

  • Corrupted storage files must be handled gracefully.

  • Given that this is meant to be included in other packages, simple and short solutions are preferred.

  • Anything else I didn't think of?

Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
  • Could you expand on what you might use this persistent storage for? – Chris K Nov 06 '16 at 16:13
  • @ChrisK The main uses case I have in mind are persistent settings. E.g. MaTeX must store the location of the pdflatex executable permanently, so I use some persistent storage for this. MATLink also need to remember if it should try to use a 32-bit or 64-bit MATLAB. Another possible use case is caching certain information. I'm primarily interested in use cases where the data that needs to be stored is small. – Szabolcs Nov 06 '16 at 17:46
  • @rcollyer Creating resources doesn't seem to be in the documentation, but it's shown here: https://www.wolfram.com/language/11/cloud-storage-and-operations/create-a-data-resource.html?product=mathematica Is this reliable and persistent though? I had trouble recalling the object I created in the last session when I played with this last time. LocalSymbol worked better, but it's buggy: http://mathematica.stackexchange.com/q/124570/12 I guess I would have to use a custom base directory with it to make sure it's local to my package. – Szabolcs Nov 06 '16 at 17:57
  • @Szabolcs the Association is detailed under the details section, but yeah, more examples are needed. I have not played with it, so I don't know its foibles. – rcollyer Nov 07 '16 at 00:25
  • 1
    What about CurrentValue[$FrontEnd, {TaggingRules, "MaTeX", "pdflatexpath"}] = ..., this is what I am doing (unles I missed the point) – Kuba Nov 07 '16 at 01:58
  • @Kuba It has a few limitations I don't like: 1. It requires a front end, which rules out use for many packages. 2. What if I have version-specific front end settings and multiple versions of Mathematica? Or I just upgrade to a new version? I'd have to set it up for each version separately. 3. Harder to migrate settings between installations. – Szabolcs Nov 07 '16 at 09:51
  • Could you mention what you tried? I guess you have used a .txt or .m file in the same directory as the package before? – masterxilo Nov 08 '16 at 23:08
  • 1
    @masterxilo OK, I'll post a solution myself, and I hope others will share theirs then. I won't be able to do it until tonight though. I prefer the file in $UserBaseDirectory/ApplicationData/PackageName, not in the package directory itself. The package directory may get removed when the package is updated. – Szabolcs Nov 09 '16 at 07:53
  • @Szabolcs Yep I agree, and initially I've also put an association in AppData/Package but I found it not flexible enough. I had a package that was complex in terms of installation/checking for updates/autoload etc, so all those checks for default values of settings when source could have been missing too, were to much. CurrentValue takes the third argument which sets and returns the default value and that is all. Fortunately I could assume the the FrontEnd will be there. – Kuba Nov 13 '16 at 17:26
  • @Szabolcs I suppose already know that but now there is a solution that does not meet only the version requirement: PersistentValue and friends. – Kuba Jun 07 '17 at 08:16

1 Answers1

7

As stated in comments above, it does not meet all your requirements. Nevertheless I find it extremely useful and applicable in many situations.

That is to use CurrentValue[$FrontEnd, {TaggingRules, "MyApp", "setting1"}] to reference your package settings.

Advantages:

  • no additional files

    Mathematica will automatically store them in init.m file. You don't even need to check whether your package is installed, which is very, very useful when it has a complex installation/autoload/updating scheme.

  • it is part of Mathematica setup, could it be more compatible?

    Tagging rules are there since V4 or something (not sure if this works there but should be no problem in V7, the earliest version I worked with)

  • it supports $FrontEndSession inheritance (see FrontEnd options)

    so you don't need to worry about switching off something that was enabled temporarily

  • automatically translates to nested rules:

    CurrentValue[$FrontEnd, {TaggingRules, "MyApp", "param1"}] = True

    translates to: {..., "MyApp" -> {"param1" -> True}}

    with syntax that feels like using Associations

  • hard to corrupt that setup since it is done automatically

  • for missing entries there is an undocumented 3rd argument of CurrentValue

    It sets that value if you try to reference a missing key:

    CurrentValue[$FrontEnd, {TaggingRules}]
    

    {..., "MyApp" -> {"param1" -> True}}

    CurrentValue[
      $FrontEnd, {TaggingRules, "MyApp", "param2"}, "defaultparam2"
    ] === "defaultparam2"
    

    True

    CurrentValue[$FrontEnd, {TaggingRules}]
    

    {..., "MyApp" -> {"param1" -> True, "param2" -> "defaultparam2"}}

Issues:

Kuba
  • 136,707
  • 13
  • 279
  • 740