Who is "me?"

*** Disclaimer - this is no longer a problem I’m actively grappling with in order to get things done, so do not put time into this out of altruism. Only mess with this for educational purposes. ***

Given a library file named “Test Library.scptd,” saved in the user’s Script Libraries folder, contents:

on return_path_to_me()
	return path to me
end return_path_to_me

When I call the “return_path_to_me()” function with the library loaded, the resulting path is reliably the path to the parent script.

ie,

set loadedAdditionsPath to ((path to home folder as text) & "Script Libraries:Test Library.scptd") as alias
set loadedTestLibrary to load script loadedAdditionsPath
tell loadedTestLibrary to set pathReturned to return_path_to_me()

Will reliably return the path to the parent script, never the path to Test Library.scptd.

That’s great.

When I skip the “load script” and do this:

tell script "Test Library" to set pathReturned to return_path_to_me()

the path returned is either the path to the parent script calling it, or the path to the library itself… whatever it feels like returning, as far as I can tell.

I spent a while trying to create reproducible test cases to submit here… “run this script and it returns the parent path, then make these changes and it returns the library path…” but I thought I was losing my mind, I was unable to get anything to work reliably. I’d have script #1 that was returning the parent path, leave it alone and try to make script #2 that returned the library path to show you, finally get script #2 to return the library path (but not sure why), then went back to script #1 that had no changes since last run, re-run it, and now it too returned the library path.

When I first started testing this to see what happened, I was getting the parent script path, and I wrongly assumed that would be consistent and went ahead and deployed a script to our users making use of this. That library function is called identically from multiple scripts, and it was returning the parent path when called from one, but the library path when called from another.

I duplicated the whole library under a different name, and called from a copy of the identical script (except calling the renamed library), and it is returning a different path between the two.

So I just made a separate library that we use the “load script” command with, that contains the handler that uses (path to me), and problem solved. But I wanted to post here anyway as a curiosity, in case any of the usual suspects want to explain what’s going on.

This post is already on the long side, but I know the standard question (and rightly so), is “what are you trying to do?” Half the questions posted, the best answer is “stop trying to do that - do something completely different to get your desired result.”

So I’ll try to be concise:

Dozens of scripts deployed, multiple people working on them. Our primitive “version control” is a “test” folder where we work on scripts, and a “deploy” folder with copies of the same scripts everyone runs.

But we also have a large and growing library, and testing that is kludgy. Deploying a broken copy of the library can take down a dozen scripts.

So I want all scripts to call the “test” version of the library iff the script is running from the “test” folder, and the “deploy” version of the library if running from the “deploy” folder. The “deploy” folder is just named “Scripts.”

Our standard company library is “ROT Additions,” So I was starting every script with:

tell script "ROT Additions" to set ROTA to get_rot_additions()

Here’s get_rot_additions() from “ROT Additions”

on get_rot_additions()
	tell application "Finder"
		set thisScriptFolder to the name of the container of (path to me)
	end tell
	if thisScriptFolder is not "Scripts" then
		try
			set pathToTestROTAdditions to ((path to home folder as text) & "Dropbox:Test:Script Libraries:ROT Additions.scptd")
			set ROTScriptingAdditions to load script file pathToTestROTAdditions
		on error
			display dialog "It appears you're running a development copy of the script from the \"Test\" folder, but the development copy of the library ROT Additions was not found." buttons {"Cancel", "Use Deployed Copy"} default button "Cancel"
			if the button returned of the result is "Use Deployed Copy" then set ROTScriptingAdditions to script "ROT Additions"
		end try
		
	else
		set ROTScriptingAdditions to script "ROT Additions"
	end if
	return ROTScriptingAdditions
end get_rot_additions()

Then all future library calls are done with “tell ROTA to…”

So all this seems to work fine as long as I load the script containing the “get_rot_additions()” handler.

We deploy scripts from the “Test” folder to the “Deploy” folder by running a Script Debugger script that archives an old copy, saves the new copy in “Test,” and saves a run-only new copy in the “Deploy” folder. It occurred to me we could always use references to the “Test” library when coding, and have the script we use to deploy also find-and-replace to the “Deploy” library for the copy saved to our “Deploy” folder. A live handler just seemed more elegant to me.

Thanks in advance for any thoughts,

Tom.

When you use a script as a Script Library – that is, you deploy it somewhere in the Script Library search path and you incorporate it either via a tell script … statement or, preferably, via a use statement, and not via load script – a path to me statement within its code should return the path to the library file.

(You second snippet points to the wrong place for a Script Library to be recognised, if that makes any difference.)

The path error was just a typo when I was putting that together to post.

I have had this handler:

on get_rot_additions()
   tell application "Finder"
       set thisScriptFolder to the name of the container of (path to me)
   end tell
   if thisScriptFolder is not "Scripts" then
       try
           set pathToTestROTAdditions to ((path to home folder as text) & "Dropbox:Test:Script Libraries:ROT Additions.scptd")
           set ROTScriptingAdditions to load script file pathToTestROTAdditions
       on error
           display dialog "It appears you're running a development copy of the script from the \"Test\" folder, but the development copy of the library ROT Additions was not found." buttons {"Cancel", "Use Deployed Copy"} default button "Cancel"
           if the button returned of the result is "Use Deployed Copy" then set ROTScriptingAdditions to script "ROT Additions"
       end try
       
   else
       set ROTScriptingAdditions to script "ROT Additions"
   end if
   return ROTScriptingAdditions
end get_rot_additions()

In use for two weeks now, installed in the Scripts Library for about 50 users, probably called over a thousand times a day altogether. It’s called like this:

tell script "ROT Additions" to set ROTA to getROTAdditionsPath()

And only 2 of those 50 users have access to the Dropbox share “Dropbox:Test”

The parent folder of the Script Library is “Script Libraries,” so if that is being returned as

the name of the container of (path to me)

then

thisScriptFolder is not "Scripts"

should be true,

load script file pathToTestROTAdditions

should fail, and 48 of the users should get the error message “It appears you’re running a development copy of the script from the "Test" folder, but the development copy of the library ROT Additions was not found” dozens of times a day. No one has seen it yet on that script.

So aside from occasionally getting the parent script path returned in my testing, I don’t see any way that script could not be returning the parent path in that instance. I don’t know how… and other scripts calling the same handler in the same way were getting the error, and I had to revert them to the previous version that didn’t call get_rot_additions().

So I don’t understand what is going on, but it seems certain to me that sometimes, “path to me” called with a “tell script” is returning the path to script that calls the handler, not the path to the library in which the handler resides.

Your handler is not going to work. In one branch you’re setting ROTScriptingAdditions via load script and in another you trying to set it to a script library – and one that’s already been loaded when you used the original tell script anyway.

When you use load script, the script is loaded as a script object into your existing script. When you use a Script Library, it’s loaded into the script’s AppleScript component, where it remains for the life of that component. You can use both if you wish, but they behave differently.

I’m not sure I see the point of your test for the container. By definition, unless you’re using the environment variable OSA_LIBRARY_PATH, the container will always be the same: Script Libraries.

Shane - I deeply appreciate your help and expertise, and you have given me amazing code of tremendous value to me. But I don’t know what to say about

That’s not what I’ve experienced. We’ve had 50 people calling this handler around 1,000+ time a day (in aggregate) for 2 weeks, and I simply can not see any way it can be returning “Script Libraries” for them. If it were, following the script logic, I do not see any possible way the script would not throw an error every time. And in my own testing, I’ve seen it return the path to the parent script. I do not know why or how, I am completely unable to show you any code that I can say will do one thing or the other in this case. Which was the whole point of my post. Sometimes “path to me” called with a “do script” is returning the path to the library, and sometimes it’s returning the path to the script calling it.

The handler when called with “do script” is definitely problematic and inconsistent in its behavior, but when done with “Load Script” it seems to be functioning really well so far and is accomplishing exactly what’s intended.

Rehash:

Goal - to have scripts use handlers from a “Test” version of our library when the running script is saved outside our deployed scripts folder, and from the “deployed” version of our library when run from our deployed scripts folder.

The individual scripts start with:


set loadedAdditions to load script [path to "ROT Loaded Additions.scptd"]
tell loadedAdditions to set ROTA to get_rot_additions_path()

The contents of Loaded Additions is the handler we’re discussing:

on get_rot_additions_path()
	tell application "Finder"
		set thisScriptFolder to the name of the container of (path to me)
	end tell
	if thisScriptFolder is not "Scripts" then
		try
			set pathToTestROTAdditions to ((path to home folder as text) & "Dropbox:ROT Scripts:Script Libraries:ROT Additions.scptd")
			set ROTScriptingAdditions to load script file pathToTestROTAdditions
		on error
			display dialog "It appears you're running a development copy of the script from outside our Scripts folder, but the development copy of the library ROT Additions was not found." buttons {"Cancel", "Use Deployed Copy"} default button "Cancel"
			if the button returned of the result is "Use Deployed Copy" then set ROTScriptingAdditions to script "ROT Additions"
		end try
		
	else
		set ROTScriptingAdditions to script "ROT Additions"
	end if
	return ROTScriptingAdditions
end get_rot_additions_path

Our standard company library, “ROT Additions,” installed in “Script Libraries,” contains this handler:

on check_this_scripting_addition()
	tell application "Finder"
		display dialog "The link to ROT Additions worked!"
	end tell
end check_this_scripting_addition

In the “Test” copy of the library, I changed that to this:

on check_this_scripting_addition()
	tell application "Finder"
		display dialog "The link to the TEST copy of ROT Additions worked!"
	end tell
end check_this_scripting_addition

and save it without deploying it.

Now I make a new script:


set loadedAdditions to load script [path to "ROT Loaded Additions.scptd"]
tell loadedAdditions to set ROTA to get_rot_additions_path()
tell ROTA to check_this_scripting_addition()

I save it at any path in a folder named “Test.” Run it, and it displays a dialog that says “The link to the TEST copy of ROT Additions worked!” I change the name of the parent folder to “Scripts,” run it, and it displays a dialog that says “The link to ROT Additions worked!”

That is the desired functionality I’m trying to achieve.

I have tried several handlers this way, but certainly not all functions with all scripts. Since I have seen “path to me” return different results based on whether it was called with a “tell script” or a “load script,” maybe I’ll find other exceptions where we get different behavior from other handlers in our library depending on whether the handler was called with “tell script” or a “load script.” Is there something you think I should be watching out for? Is this whole thing a bad idea? It seems to be working well so far.

But I can always go back to modifying our Script Debugger “Deploy Script” script, so that it does a find-and-replace on which library to use when a script is deployed, instead of trying to have a handler determine which version of the library to use.

You should get different results – they’re quite different things.

Stop trying to treat loaded scripts and Script Libraries as the same.

If you want to make decisions about which version of code is used at run time, use load script.

It’s not clear to me if the “Script Libraries” folder which isn’t the Dropbox one is in one of the system-recognised locations as listed here. The second script in post #1 suggests that it isn’t, as Shane pointed out. tell script “ROT Additions” will only work if the system can find the “ROT Additions” script.

That aside, if a running script’s going to contain the get_rot_additions() call anyway, why not have it pass its path as a parameter?

tell script "ROT Additions" to set ROTA to get_rot_additions(path to me)
on get_rot_additions(script_alias)
	tell application "Finder"
		set thisScriptFolder to the name of the container of (script_alias)
	end tell

	-- Blah blah blah

end get_rot_additions

Nigel

Thank you! I don’t know why I didn’t think of that, it seems so obvious as soon as you say it. That’s a completely safe way to not care what “me” refers to in a [library or loaded script]. Great idea.

The script we’re calling as a library is effectively in the standard user library. The “Script Library” folder in every user’s ~/Library/ folder is actually an alias to a Script Libraries folder on a shared Dropbox, but that works to call libraries, OSX follows the alias to the library.

Shane

I get that you’re saying to “stop trying to treat loaded scripts and Script Libraries as the same” but I’m not sure why since it seems to be working fine in testing. I trust that you know a lot more about this than me, though, so I’ll take your advice.

If it’s not a safe way to operate, is there a reason it’s better to replace it with a “Load script” in all instances, rather than to keep a test version of our library under a different name in the “Script Library” folder and have the function return a reference to a different script library?

So each script would do this:

tell "ROT Additions" to set ROTA to get_rot_additions_path(path to me)

And that handler will be this in our library:

on get_rot_additions_path(parentPath)
	tell application "Finder"
		set thisScriptFolder to the name of the container of parentPath
	end tell
	if thisScriptFolder is not "Scripts" then
		try
			set ROTScriptingAdditions to script "TEST ROT Additions"
		on error
			display dialog "It appears you're running a development copy of the script from outside our normal \"Scripts\" folder, but the development copy of the library ROT Additions was not found." buttons {"Cancel", "Use Deployed Copy"} default button "Cancel"
			if the button returned of the result is "Use Deployed Copy" then set ROTScriptingAdditions to script "ROT Additions"
		end try
		
	else
		set ROTScriptingAdditions to script "ROT Additions"
	end if
	return ROTScriptingAdditions
end get_rot_additions_path

FWIW, although aliasing of a Script Libraries folder works, it is unsupported.

Well you seemed to be saying it wasn’t working.

Lots of stuff “works” in a particular situation. But a Script Library is theoretically a shared resource, whereas a loaded library is not. There are differences. Change a property in a Script Library, for example, and other scripts run in the same AppleScript component instance will see it.

With load script, it’s fine to load stuff willy-nilly whenever you want to use it. With Script Libraries, I think it’s better to take a more disciplined approach most of the time. Load them as properties in use statements at the beginning of your scripts, and write them with the knowledge that they are potentially shared resources in mind. Having said that, loading different libraries depending on circumstances is not an unreasonable thing to do, and obviously requires a different approach.

What concerned me was that your original code was setting ROTScriptingAdditions to either a Script Library or a loaded script, depending on circumstances, as if they were equivalent. Your latest code doesn’t do that, so it looks fine. (Apart from resorting to the Finder for parsing a path, that is. It’d be quicker and safer to use text item delimiters and parse it yourself.)

Thanks Shane! Will stick with Libraries as Libraries and Loaded Scripts as Loaded Scripts.

The “path to me” thing wasn’t working reliably in Libraries, but Nigel’s suggestion bypasses that issue.

For me, setting a variable to a reference to a Library for the deployed copy and a loaded script for the test copy was working with all the handlers I’d tested it with. But since I already knew “me” worked differently between the two methods, and as you pointed out, properties could also, it’s better to just set the variable to different libraries depending on the situation. Who knows what else is out there that could create some stupid, hard-to-diagnose bug some day where something worked in the test version of our library but broke when it was deployed, and the difference was just in that change in how we were calling it?

This is easy to do, the only real change it requires is moving the “test” version of the library out of the dropbox shared by developers and into the standard library shared with everyone, and then change out Script Debugger “deploy script” script to just save under the different name when deploying a library.

Thanks,

Tom.