errOSAInternalTableOverflow Error: tips on Using Script Libraries


#1

Please, I’m looking for some help/tips on breaking an applet into multiple scripts/libraries.

Background

I just encountered the errOSAInternalTableOverflow error with one of my applets. Mark has helpfully explained, as I suspected, that I have too many lines of code in the applet and need to break it up.

This is actually v 2.0 of this project. The earlier version was broken up into different scripts but I decided to rewrite it all as one script because of issues that were arising from having it split up, such as problems with displaying progress indicators and tracking bugs. Admittedly the way I was doing it was pretty messy – I was using the load script command rather than the use script statement – and the use script statement appears to solve many of those issues.

The things I’m still concerned about are:

  1. library handlers calling handlers from other libraries or from the main script; and
  2. sharing global variables/properties.

Library handlers calling handlers from other libraries or from the main script

By way of explanation, it seems I’ll need to use more than one script library to avoid errOSAInternalTableOverflow.

So let’s say I create two scrips “lib1.scpt” and “lib2.scpt” both inside the “Script Libraries” folder inside my applet’s Resources folder. This applet is being distributed among work colleagues so ideally everything (including any libraries) will be contained in the one bundle.

Can lib1 contain handlers that call handlers from lib2 or vice-verser? If so, how is it done? I just tried to test it by including a use script “lib2” statement in lib1 but it got an error couldn’t find script “lib2”.

Sharing global variables/properties

The applet is designed to be used by multiple users on different machines all sharing the same pool of data. The data is saved in a shared folder. I’ve been comparing different ways to store the data – it’s currently written and read to disk using AS’s read/write commands with a somewhat convoluted way of ensuring there are no sync conflicts. All the other handlers in the applet basically do stuff with that shared data and, if they want to save changes to the data, they call another handler that writes the data to disk in a way that avoids sync conflicts.

One of the ways I avoid sync conflicts is that whenever a handler is going to do something significant, it calls a refreshData() handler that reads the shared data and, if it has been modified, updates the myriad global variables that represent that data. Obviously there’s a fair bit of optimisation that goes into this – too frequently and things run too slow, too seldom and things risk getting out of sync.

Currently thanks to global variables and all the code being contained in one script the process of updating/refreshing the data is pretty simple. But seems like all that will change after splitting it into different scripts.

From my very limited reading/testing it seems like the only way to do it is to:

  • declare the global variables as properties in each script; and
  • expressly set the value of the variable for each script/library whenever it is changed (e.g. set lib1’s x to x, set lib2’s x to x).

In particular this second part seems like it’s going to be a nightmare especially if I need more than one library.

Does anyone please have any tips or recommendations for dealing with this?


#2

So I have read the documentation on inheritance which looks like it will help, but I can’t seem to get it to work in practice.

My library script isn’t able to get properties from the main script which calls it. I tried omitting any reference to a parent in the library script in case the main script that uses the library would be implicitly defined as its parent. That didn’t work so I explicitly defined the library scripts parent to be the main script that uses the library. That didn’t work either.

Below is my main script which is saved as an applet. Its name is “Used Script” (don’t ask – creative juices are running low). The library script is called “testHandlers.scpt” and is saved in the “Script Libraries” folder in the applet’s “Resources” folder. The names are fine – if not I would get compile errors (which is what happened initially, until I figured that part out).

macOS 10.14.2

Main Script

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
use OBLibrary : script "testHandlers"

property n : 500

try
   OBLibrary's testThisHandler()
on error msg
   display alert msg
end try

Library Script

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
property parent : "Used Script"

to testThisHandler()
	set i to 0
	updateProgress given progressDescription:"Testing progress indicator", totalSteps:my n, stepCount:i
	repeat with i from 1 to my n
		updateProgress given stepCount:i
	end repeat
	updateProgress given progressDescription:"", totalSteps:0, stepCount:-1
	display dialog "This is a dialog" with title "Test Handler"
end testThisHandler

on updateProgress given progressDescription:progressDescription : missing value, additionalDescription:additionalDescription : missing value, totalSteps:totalSteps : missing value, stepCount:stepCount : missing value
	delay 0.02
	if progressDescription is not missing value then set my progress description to progressDescription
	if additionalDescription is not missing value then set my progress additional description to additionalDescription
	if totalSteps is not missing value then set my progress total steps to totalSteps
	if stepCount is not missing value then set my progress completed steps to stepCount
end updateProgress

(Shane Stanley) #3

Script Libraries — that is, scripts loaded via a use statement — are potentially a shared resource. If you want them to know about your calling script, you will have to pass them a reference they can use (ie, pass me).

(Can you also please format code blocks by wrapping them in lines consisting of just three back-tick characters. I’ll edit the above.)


#4

Thanks for your reply, Shane!

Apple’s documentation says that child scripts inherit all the properties and handlers of their parent script.

If I specify the parent of a script library (i.e. a script being called by a use statement) to be the script that is calling it by the use statement then why wouldn’t it inherit? Or can a script library not be a child of the script that is calling it for some reason?


(Shane Stanley) #5

A Script Library can’t be a child. As I said, they’re potentially a shared resource.

Most of the documentation about inheritance predates Script Libraries, and uses the term script library to refer to a script loaded using load script.


#6

For the record the documentation I linked to was updated after Mavericks and didn’t use the term script library to refer only to scripts loaded with load script. The following is from the page I linked to:

A top-level script object saved in a Script Libraries folder becomes a script library usable by other scripts. Libraries let you share and reuse handlers, reorganize large scripts into a set of smaller libraries that are easier to manage, and build richer, higher-level functionality out of simpler libraries.

Note: Libraries are supported in OS X Mavericks v10.9 (AppleScript 2.3) and later. To share properties and handlers between scripts in prior OS versions, use the load script command as described in Libraries using Load Script.

I guess what I overlooked was that a script library is a top-level script and its parent will always be AppleScript. Well, to be more accurate, I think I understood that, but what I didn’t realise was that you can’t specify the parent of a top-level script to be something other than AppleScript, unlike other scripts whose parent can be defined to be any other script (or even non-script objects).

Anyway, I guess there’s probably nothing for it but to re-write everything so that the global variables are passed to the library’s handler when called. And I guess I’ll have to spend some time trying to structure the various libraries so each library’s handler doesn’t contain a call to a handler outside that library.

But if anyone has any ideas of how to do things differently, please let me know.


#7

Could I have one library access another library’s handlers by using the load script command (i.e. library1 includes an implicit run handler that loads another library and stores it as a global variable)? I believe library scripts usually don’t include a run handler and suspect the use statement doesn’t call the run handler if it had one, but I’m just trying to find away around this arbitrary limit on the length of each script file. Maybe if rather than in a run handler, the library had a user-defined handler that loaded the other library and stored it in a global variable, and my main script could call that handler when it first launched.

I’m curious what others have done to work around this as I imagine that other people have run up against this issue before.


(Shane Stanley) #8

Have a look at the first post here: Property values not being globally available


(Shane Stanley) #9

That’s what I was suggesting in my comment about libraries being potentially shared resources.

Typically, an application will have a single AppleScript component instance, and one you load a Script Library, it remains cached in that instance. If you have an application that runs more than one script — FastScripts, InDesign, BBEdit, for example — al the scripts will share the same component, so things like text item delimiters and even variable casing will be common to them all, and once one loads a Script Library, it remains loaded. If other scripts try using the same library, or even an updated version of it, they will end up dealing with the same cached instance of the library.

Script Editors (and Apple’s Script menu) are different in that they generate a new AppleScript component for each script — in fact, for each compile. (This wasn’t always the case.)


#10

Thanks, Shane! I had read that before and got part of the answer from it, but missed the key part where the main script passes one library to the other library to be stored as a property. And then I got distracted with the hunt to reproduce the bug through Swedish language and date time localisation.

This seems like it will work for me. It will still be a headache managing variables, but it will at least mean I can share handlers which was proving to be the more critical issue.


#11

Also, looks like I’m going to have to rewrite even more because I just discovered your brilliant Myriad Tables Lib, @Shane. That’s going to make things sooooo much better, aside from the fact that it now gives me more homework. Thanks for creating such a fantastic resource!


#12

It’s not quite working as expected. I’ve restructured my scripts but am encountering an execution error.

The relevant parts are as follows

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
use application "Numbers" without importing

-- libraries
use OBUtility : script "OB Utilities"
use OBBasic : script "Time and Matter Management"
use OBTrust : script "Trust Accounting"
use TablesLib : script "Myriad Tables Lib" -- Thanks, Shane!

-- [some more property declarations that aren't relevant and are omitted]

on run
	-- share libraries
	set OBUtility's OBMain to me
	set OBUtility's OBBasic to OBBasic
	set OBUtility's OBTrust to OBTrust
	set OBBasic's OBUtility to OBUtility
	set OBTrust's OBUtility to OBUtility
	set OBTrust's OBBasic to OBBasic

-- [declare more variables and user defaults etc]
end run

the OBUtility (script “OB Utilities”) has properties called OBMain, OBBasic, OBTrust, etc.

On run it’s encountering an error “Unable to make OBUtility into type reference.” :thinking:

If you could offer any pointers that would be amazing. Thank you! :blush:


(Shane Stanley) #13

Honestly, I think you’re asking for continual pain. I can see how you might want to load, say, one utility library into the others, but swapping everything like that makes things really hard to keep track of.

Try making your man script more a controller, just passing out smaller tasks to the other libraries.