Library loading oddity

Hi,

I encountered some issues when loading libraries and I wonder if anyone here can help me understand what happened. I’ll explain with a few scripts.

this is the library code. Simplified for clarity. This is saved as a script bundle in the ~/Library/Script Libraries/ folder.

use AppleScript version "2.5"
use framework "Foundation"
use scripting additions

property testProperty : 1
property NSCurrentApplication : a reference to current application
property NSURLreference : a reference to NSCurrentApplication's class "NSURL"

on NSURL_fileURLWithPath(finderobject)
	try
		NSURLreference's fileURLWithPath:finderobject
	on error errorText number errornumber partial result errorResults from errorObject to errorExpectedType --Unhandled errors in the handler are caught here.
		error "NSURL_fileURLWithPath handler" & errorText number errornumber partial result errorResults from errorObject to errorExpectedType --Declare the library's handler as the source of the error.
	end try
end NSURL_fileURLWithPath

This script loads that library and can demonstrate the error I was getting.

use AppleScript version "2.5"
use framework "Foundation"

--property testProperty : 1

property NSCurrentApplication : a reference to current application
property NSURLreference : a reference to NSCurrentApplication's class "NSURL"
use testLibrary : script "testLibrary"


property NSURL_fileURLWithPath : testLibrary's NSURL_fileURLWithPath
NSURL_fileURLWithPath("/Users/paulskinner/Desktop/targetFolder")

Running the test script results in an error:
NSURL_fileURLWithPath handler*** -[BAGenericObjectNoDeleteOSAID fileURLWithPath:]: unrecognized selector sent to object <BAGenericObjectNoDeleteOSAID @0x600000de67e0: OSAID(2) ComponentInstance(0x85007a)>

Weird things that I discovered while testing:

  1. Un-commenting the testProperty line ( “–property testProperty : 1” ) allows this script to correctly return an NSURL. If ALL properties in the two scripts are matched by properties in the other script this somehow allows proper function.

  2. Using the loaded handler directly does not result in an error. This only occurs when setting a property to the handler and then calling that property.

  3. properties required in the loaded handler must be present in the script that uses the loaded library.

  4. Moving the loading of the library to above the property declarations resolves this issue.

I suppose that this is all as expected, but I am unclear on the scoping issues.

Questions I’m left with :

  1. Why does this script succeed when all properties are present in both scripts even when some of those properties are unused?

  2. Why does loading the handler into a property CRUCIALLY affect the requirements for calling this handler? Why does this script succeed when the handler is called directly and fail when the property holding the handler is called?

If anyone is still around at this point I’ll explain that this isn’t just me futzing around with a strange construct looking for trouble. I have multiple libraries that successfully utilize this construct and found only this particular function ( “fileURLWithPath:finderobject” ) causes this strange error code to bubble up from the depths of ASObjC.

The error seems to be the line
“property NSURL_fileURLWithPath : testLibrary’s NSURL_fileURLWithPath”
You seem to be trying to set a property variable to the name of a handler.
Not sure you can do that.
I commented out that line and it works.

use AppleScript version "2.5"
use framework "Foundation"

--property testProperty : 1

property NSCurrentApplication : a reference to current application
property NSURLreference : a reference to NSCurrentApplication's class "NSURL"
property testLibrary : script "testLibrary"

--property NSURL_fileURLWithPath : (script "testLibrary")'s NSURL_fileURLWithPath
testLibrary's NSURL_fileURLWithPath("/Users/paulskinner/Desktop/targetFolder")

That line is not the source of the error, but certainly is crucial to the issue. Without it I can avoid the error. But, fortunately for me, you can indeed assign a loaded library’s handler to a property and have it work perfectly!

This code executes just fine.

use AppleScript version "2.5"
use framework "Foundation"
use testLibrary : script "testLibrary"
property NSCurrentApplication : a reference to current application
property NSURLreference : a reference to NSCurrentApplication's class "NSURL"
property NSURL_fileURLWithPath : testLibrary's NSURL_fileURLWithPath

NSURL_fileURLWithPath("/Users/paulskinner/Desktop/targetFolder")
-->(NSURL) file:///Users/paulskinner/Desktop/targetFolder/

The property isn’t assigned the name of the handler, but the handler itself. In fact, returning NSURL_fileURLWithPath yields the compiled handler code.

I’m really just trying to determine what happens to the scope of the loaded handler and the properties within the library.

I use this assignment of a handler to a property extensively in my libraries. It allows me to bring loaded handlers to the top level of another library. Removing the need for the enclosing library’s reference.

The other thing I would avoid is using the same property names in both your calling script and your library script.

The reason is, since you are using a “use” statement, it causes a scope problem when you now reference the property. Which one gets used?
If you want to have the same property names in both, then don’t use a “use” statement, and instead use a full reference to the lib’s property when needed.

1 Like

I’m not sure that I follow. The “use” statement loads the library script. All the library’s handlers are inaccessible to the script they are loaded into. You have to call them using a reference to their enclosing script i.e.“loadedLibrary’s handlerOne”.

There is no “handlerOne” in the script loading the library for my property name to conflict with. Or perhaps I’m missing something?

Thanks for taking time to look at this! I appreciate it.

Edit: I’m revisiting your comment paying attention to the NS properties and not just the handler property.

Edit2: I believe you may have hit on the root cause! Not that the property’s name conflict, but the library’s properties to begin with. Removing the properties entirely from the library and fully referencing " current application’s class “NSURL”'s fileURLWithPath:finderobject" resolves the issues.

I’ll confirm this in a larger codebase, but I believe that references to “current application” and its NS classes is what is getting broken when loading in different contexts.

I’m referring to the properties.

But since you are talking about handlers, if you don’t use a use statement, you get to the Lib’s handlers like so…

(script "testLibrary")'s NSURL_fileURLWithPath("/Users/paulskinner/Desktop/targetFolder")

or like I did in my version above…

property testLibrary : script "testLibrary"

testLibrary's NSURL_fileURLWithPath("/Users/paulskinner/Desktop/targetFolder")

or with a tell command like so…

tell script "testLibrary"
	NSURL_fileURLWithPath("/Users/paulskinner/Desktop/targetFolder")
end tell

notice there is no “use” command in any of these.

Hi.

There does seems to be some confusion between the similarly named properties in the two scripts. An alternative to using …

testLibrary's NSURL_fileURLWithPath("/Users/paulskinner/Desktop/targetFolder")

… in the main script appears to be to use …

(my NSURLreference)'s fileURLWithPath:finderobject

… or …

my (NSURLreference's fileURLWithPath:finderobject)

… in the library script. This apparently clarifies inheritance issues for the handler code and works whether or not the properties are actually declared in the library script — as long as they’re declared in the main script, of course.

Theory:
If the hander’s referenced as belonging to the library script, or if it if contains explicit references to the library script’s properties, then it uses the library script’s properties. In both cases, if the properties aren’t actually declared in the library script, they’re inherited from the main script.

If the handler’s simply assigned to, and called through, a property of the main script, and it doesn’t use either my or its when referring to properties, it may or may not work.

1 Like

I don’t have an answer to Paul’s question, but I had a question about the scope of properties when calling a script library. The following scripts work as expected:

--the calling script

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

myLibrary's sayHello()
--the script library script

property theGreeting : "Hello"

on sayHello()
	display dialog theGreeting
end sayHello

However, the following scripts report an error. Is there any approach which allows the script library script to see the property in the calling script? I looked at the documentation, but it wasn’t clear on this point. Thanks.

--the calling script

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

property theGreeting : "Hello"

myLibrary's sayHello() --> AppleScript Execution Error. The variable the Greeting is not defined.
--the script library script

on sayHello()
	display dialog theGreeting
end sayHello
1 Like

Hi @peavine.

I may have deceived myself in one respect when looking at this yesterday. Today, the only way I can get a handler in a library script to inherit a property from the main script is to use my in the library script as I described above, but also to load the handler into one of the main script’s variables, as Paul was doing. So:

--the script library script

on sayHello()
	say (my theGreeting)
end sayHello
--the calling script

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
use myLibrary : script "TestLibraryTwo"
property theGreeting : "Hello"

set sayHello to myLibrary's sayHello -- Top-level variable here. Alternatively a property or global.
sayHello() --> Audible "Hello"

I guess the lesson is not to fool around trying to share properties and handlers with library scripts, but to tell libraries explicitly to execute any handlers they provide, passing any relevant values from the main script as parameters. It’s good OOP. :slightly_smiling_face:

1 Like

Thanks Nigel for the solution. I wholeheartedly agree with what you say above, but it’s always good to understand how things works.

I can’t improve on the comments made so far.

I do something similar which has worked well for me for some years:

In every Library FIle (which I did save as bundles until I found that unnecessary), I include a property M:

property M : missing value

In the main script I have the following (for example):

use C : script "CaptureOne_1601"
use E : script "User Edit 1602"

set C's M to a reference to me
set E's M to a reference to me

A handler in the Library file can then access a handler"userprefs()" in the main script as “M’s userprefs()”

For debugging purposes it is sometimes necessary to temporarily move a handler to the main script. This can easily be done as follows:

Lets say the handler “LogSetup(x,y,z)” in “User Edit 1602” has to be debugged in detail.

First I copy all of LogSetup(x,y,z) to the end of the Main script

The I insert a line into the Library version as follows

on LogSetup(x,y,z)
return M's LogSetup(x,y,z)

  blah
  blah 
  blah 
  etc 
return

Once the contents of Main’s LogSetup(x,y,z) have been changed, I copy it to overwrite the handler in the Library

It gets a little more tedious, but is still manageable, if the handler being debugged refers to M or refers to other handless in the library.

For some debugging operations I may define M in the main handler as well, which has not caused any errors, but may increase menory size and decrease speed

property M : a reference to me