Difference between Script Debugger and Script Editor in terms of loading another script

Hi, I’m a Script Debugger (SD) holdout and new here because I was overwhelmed by the UI and now I’m willing to try learning it again after getting most of my scripts (hundreds) to where I want them to be and I’m sick of Script Editor app (SE) crashing each time I need to close a tab.

I’m building an AppleScript library that allows me (hopefully I can share it with the public soon when it becomes stable enough to run using SD because it is probably what most of the serious AppleScripters use to write AppleScripts, just my assumption).

My issue with SD is when I re-run some AppleScripts, I get this undefined error where a library reference, which I usually declare globally with the intention to reuse, gain some optimization and make it accessible inside handlers, whenever I re-run a script without making any changes. So it works fine as long as I change the script. I don’t have this problem when using SE but like I’ve said, I’m tired of that app crashing each time I need to close a tab. Can someone please help me understand what is going on and why am I experiencing this error? The issue only happens on some scripts, not all of them. So it could be something I need to tweak for those affected.

Here’s how I design my scripts.

I have a highly re-used script I call std.applescript which takes care of importing other scripts. std.applescript contains this handler:

on import(moduleName)
    set scriptObj to script moduleName
	    if not initialized of scriptObj then
	        scriptObj's init()
	    end if
    end try
end import

On a client script, I would have something structured such as:

set std to script "std"
set logger to std's import("logger")'s new("hello")
logger's info("Hello AppleScript Core!")

The above script works fine, even on re-run.
However on another script:

set std to script "std"
set inspector to std's import("app-inspector")'s new()
set savedAppDetail to inspector's getSavedAppDetails() -- Fails here with the error "mapLib is not defined"

The app-inspector.applescript would contain:

global std, mapLib
property initialized : false

on init()
    if initialized of me then return
    set initialized of me to true

    set std to script "std"
    set mapLib to std's import("map")

So the mapLib is indeed used inside the getSavedAppDetails handler, but why is it not accessible during the subsequent run given it is declared globally and should have been initialized inside the init during the import?

I appreciate any feedback on how I can improve my posting so that it is clear and helpful to others. Thank you in advance.

From first glance, I have a couple of notes:

Regarding the applications:

  • Recent versions of Script Debugger use a separate AppleScript OSA component instance for each tab (in order to protect against namespace pollution from other scripts).
  • Script Editor uses a shared instance of the component instance, and separate scripts can affect each other in subtle and dangerous ways when you try to do something too exotic with AppleScript (your loading mechanism, while very neat, definitely qualifies as exotic).

Regarding script library loading:

The recommended way to load a script library is with a use statement (which dynamically loads the script library by name, and is a compile-time instruction, similar to a property statement):

use std : script "std"

In the grand scheme of things, this is a newer addition to the language (macOS 10.9). This mechanism is being (ab)used when you specify e.g. script "std", except that this is happening an run-time. Previously, the only option was to use a load script command from Standard Additions, which required an absolute path & predates the library loading mechanism.

The use statement dynamically loads the library, and is shared amongst all scripts in the same component instance.

Regarding global variables:

Traditionally, in most environments, the run-time values of all top-level variables (such as declared global variables) are actually stored in the compiled script after execution as an old, brittle method of persistence. This has become progressively less reliable as time goes on because of issues with a self-modifying script breaking code signatures. Script Debugger 8 has disabled persistence for this reason.

Your code:

I suspect you’re getting some kind of interplay between global variables & script file persistence (some of your script libraries are being stored in an initialized state in your client scripts after the first run when using Script Editor). For debugging, you can unset your script library references (i.e. set mabLib to missing value or similar) at the end of execution when using Script Editor to “disable” persistence.

FWIW, generally, you only want to load a script library once.

1 Like

If you’re running on an M1 or M2 Mac, or using a recent version of macOS, applets no longer persist globals. That’s because code-signing is required, and the way AppleScript stores globals (and top-level variables) modifies the actual script on file, which would render applets un-usable.

To make the editing experience more closely match what happens in applets, Script Debugger 8 doesn’t persist globals when you run them.

1 Like

Thank you for sharing your knowledge, yes I’m on Apple silicon, 12.6 Monterey, and planning on upgrading to Ventura very soon.

The book AppleScript: The Definitive Guide (while very old, predating script libraries) is the best reference I’ve come across for some of the really weird AppleScript behaviour such as this.

1 Like