Python Interpreter in Dopus's Scripting Interface

So I noticed the command modifiers for using a script language a while back, but didn't have any use for them, since I'm not a fan of VBScript. I later tried substituting jscript for the vbscript argument, and although it worked fine, the Active Scripting implementation of Javascript doesn't support object references, so it's a bit limited in what it can do. The other night, however, I remembered that Python has a Active Scripting engine with the pywin32 package. If, like me, you use ActivePython, your Active Scripting engine may not work at the moment. If you get a DLL load failure error (or something along those lines) when you attempt to use the engine, go to "Lib\site-packages\win32comext\axscript\client" in your python directory, and run "pyscript.py". So after a quick lookup of the ProgID, and some modifications to an existing script, I threw this together for testing purposes.

<?xml version="1.0"?>
<button backcol="none" display="both" textcol="none">
	<label>Dopus Console</label>
	<tip>Python interpreter embedded in Directory Opus.</tip>
	<icon1>#DOpus9:cli</icon1>
	<function type="normal">
		<instruction>@script python</instruction>
		<instruction>import socket</instruction>
		<instruction>import code</instruction>
		<instruction>import sys</instruction>
		<instruction />
		<instruction>class DopusConsole(code.InteractiveConsole):</instruction>
		<instruction>	def __init__(self, rfile, wfile, locals=None):</instruction>
		<instruction>		self.rfile = rfile</instruction>
		<instruction>		self.wfile = wfile</instruction>
		<instruction>		code.InteractiveConsole.__init__(</instruction>
		<instruction>			self, locals=locals, filename=&apos;&lt;DopusConsole&gt;&apos;)</instruction>
		<instruction />
		<instruction>	def raw_input(self, prompt=&apos;&apos;):</instruction>
		<instruction>		self.wfile.write(prompt)</instruction>
		<instruction>		return self.rfile.readline().rstrip()</instruction>
		<instruction />
		<instruction>	def write(self, data):</instruction>
		<instruction>		self.wfile.write(data)</instruction>
		<instruction />
		<instruction />
		<instruction>DOpus.OpenOutputWindow()</instruction>
		<instruction>DOpus.OutputString(&apos;\nDopusConsole listening on port 7777&apos;)</instruction>
		<instruction>DOpus.OutputString(&apos;&gt;&gt; telnet 127.0.0.1 7777\n&apos;)</instruction>
		<instruction>netloc = (&apos;&apos;, 7777)</instruction>
		<instruction>servsock = socket.socket()</instruction>
		<instruction>servsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)</instruction>
		<instruction>servsock.bind(netloc)</instruction>
		<instruction>servsock.listen(5)</instruction>
		<instruction>print &apos;listening&apos;</instruction>
		<instruction>sock, _ = servsock.accept()</instruction>
		<instruction>print &apos;accepted&apos;</instruction>
		<instruction />
		<instruction>rfile = sock.makefile(&apos;r&apos;, 0)</instruction>
		<instruction>sys.stdout = wfile = sock.makefile(&apos;w&apos;, 0)</instruction>
		<instruction>console = DopusConsole(rfile, wfile,locals())</instruction>
		<instruction>console.interact()</instruction>
	</function>
</button>

Once you connect into the interpreter, you'll have access to all your normal "command" api's, although it also looks like you now have access to some more advanced things, like events:

IDOpusScriptEvent_Function Class:
		      455 : "OnBeforeFile",
		      456 : "OnAfterFile",
		      452 : "OnAfterFunc",
		      451 : "OnBeforeFunc",
		      453 : "OnBeforeInstruction",
		      454 : "OnAfterInstruction",

IDOpusScriptEvent_Lister Class:
		      480 : "OnFormatChange",
		      478 : "OnChangeDir",
		      479 : "OnStateChange",

DOpusDMessengerEvents Class (The Messenger support) has events like:
		     def OnContactAddedToGroup(self, hr=defaultNamedNotOptArg, pMGroup=defaultNamedNotOptArg, pMContact=defaultNamedNotOptArg):
		     def OnContactBlockChange(self, hr=defaultNamedNotOptArg, pMContact=defaultNamedNotOptArg, pBoolBlock=defaultNamedNotOptArg):

And a bunch of others. I'm also noticing some function that appear to allow for manipulation of buttons, so it may become possible to create dynamic buttons that are hidden and shown depending on the context of the lister. (The filetype selected, folder path, some parsed metadata, etc) I haven't gotten a chance to play with it all that much, but I have seen some cool API. Hopefully in the next week or two, when I have more time, I'll sit down and write some wrapper classes to make the API a bit more accessible. Here's a quick example:

# Get the window size.
mySize = (Func.Lister.Width, Func.Lister.Height)
print mySize 
# printed (800, 746)

# Or resize the window
Func.Lister.Width = mySize[0] + 100
# My lister's width is now 900.

I didn't implement any exit procedure in the shell, since it was just a quick test of a concept, so to stop the script, go into the interpreter, and enter exit(0), then just ctrl+c to throw a keyboard interruption. Should look like this:

>>> exit(0)
^C

Slightly related, I also confirmed that it's possible to use perl to script you rename scripts/buttons (granted that you're using ActivePerl, which comes with the PerlScript Active Scripting Engine) They end up looking something like this:

@script perlscript
use Win32::OLE;

open my $logfile, ">>", "my.log";
$DOpus->OpenOutputWindow();

sub logm {
	my $str = @_[0];
	print $logfile $str . "\n";
	$DOpus->OutputString($str . "\n");
}

my $listers = $DOpus->Listers();
logm("There are " . $listers->Count() . " listers up.");
logm("Src Path: " . $listers->SourcePath());

But yeah, that's about it. Figured it warranted sharing.

This is nice.

perl scripting is mentioned, with a quick template, here, as well as in some of the docs. I'm actually surprised it is not used or referenced more here. I've posted a couple of examples helping w/rename scripts.

Thanks for sharing!

[Edit: This info is out of date now, as it pre-dates more widespread scripting support.]

Note that @script is only officially supported as part of rename commands, and I don't think the events you found on the object are officially supported/documented (i.e. there's no guarantee that OnBeforeFile etc. will work in the future, or now for that matter).

Woops, my mistake. Yeah, I wish I had read and found out a little bit sooner. My toolbar is filled up with links to a bunch of different batch scripts, all utilizing those stupid little %~ escape characters.

Understandable, but I figure I don't often get to work with COM interfaces nowadays, so I might as well make an excuse to play around with them. If it turns out that it doesn't work in the future (or now even), I'll at least get some practice out of i t.