001/* An actor that executes a Python script. 002 003 Copyright (c) 2003-2014 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 */ 027package ptolemy.actor.lib.python; 028 029import java.io.File; 030import java.util.HashMap; 031import java.util.Iterator; 032import java.util.Locale; 033import java.util.Properties; 034 035import org.python.core.PyClass; 036import org.python.core.PyException; 037import org.python.core.PyJavaType; 038import org.python.core.PyMethod; 039import org.python.core.PyModule; 040import org.python.core.PyObject; 041import org.python.core.PyString; 042import org.python.core.PySystemState; 043import org.python.util.PythonInterpreter; 044 045import ptolemy.actor.TypedAtomicActor; 046import ptolemy.actor.process.TerminateProcessException; 047import ptolemy.kernel.CompositeEntity; 048import ptolemy.kernel.Port; 049import ptolemy.kernel.util.Attribute; 050import ptolemy.kernel.util.IllegalActionException; 051import ptolemy.kernel.util.NameDuplicationException; 052import ptolemy.kernel.util.Settable; 053import ptolemy.kernel.util.StringAttribute; 054import ptolemy.kernel.util.Workspace; 055import ptolemy.util.ClassUtilities; 056import ptolemy.util.StringUtilities; 057 058/////////////////////////////////////////////////////////////////// 059//// PythonScript 060 061/** 062 An actor of this class executes a Python script. There are two versions 063 of this actor provided in the Vergil libraries. The one called 064 "PythonActor" has an input port and an output port; to view or edit 065 its Python script, look inside the actor. The second version is 066 called "PythonScript" and has no ports; to view or edit its Python 067 script, select Configure (or double click on the icon). 068 069 <p> Upon creation, this actor has no ports, and no parameters other than 070 {@link #script script}; The <i>script</i> parameter has visibility 071 EXPERT, and therefore does not normally show up in a configure dialog 072 for the actor. To make the script visible and editable, you have two 073 options. Including an instance of an attribute of class 074 TextEditorConfigureFactory (with its <i>attributeName</i> parameter 075 set to <i>script</i>) results in behavior like that of the Vergil 076 "PythonScript." That is, to edit the script, you Configure the actor. 077 If instead you include an instance of TextEditorTableauFactory, 078 then to edit the script you look inside the actor. Use the latter 079 if you wish to add additional attributes to the actor and hide the 080 script from the users. Use the former if the script is the main 081 means by which users interact with the actor.</p> 082 083 <p> The functionality of an actor of this type is given by a Python script. 084 As an example, a simplified version of the 085 {@link ptolemy.actor.lib.Scale Scale} 086 actor can be implemented by the following script:</p> 087 <pre> 088 1. class Main : 089 2. "scale" 090 3. def fire(self) : 091 4. if not self.input.hasToken(0) : 092 5. return 093 6. s = self.scale.getToken() 094 7. t = self.input.get(0) 095 8. self.output.broadcast(s.multiply(t)) 096 </pre> 097 098 <p>Line 1 defines a Python class Main, which matches the value of the 099 <i>jythonClassName</i> parameter. An instance of this class is created when the 100 actor is initialized. Line 2 is a description of the purpose of the 101 script. Lines 3-8 define the fire() method, which is called by the 102 {@link #fire() fire()} method of this actor. In the method body, 103 <i>input</i> and <i>output</i> are ports that have to have been added 104 to the actor, and <i>scale</i> is a parameter that has to have been 105 added to the actor (these can be added in the XML that defines the 106 actor instance in an actor library). The Main class can provide other 107 methods in the {@link ptolemy.actor.Executable Executable} interface 108 as needed.</p> 109 110 <p>In the script, use <code>self.actor</code> to access the actor. For example, 111 <code>self.actor.getDirector()</code> returns the current director of the 112 actor. For debugging, use <code>self.actor.debug(someMessage)</code>. The 113 final message sent to the debug listeners of the actor will have the string 114 "From script: " inserted at the beginning. To avoid generating the debug 115 message when there are no listeners, use:</p> 116 <pre> 117 if self.actor.isDebugging() : 118 self.actor.debug(someMessage) 119 </pre> 120 121 <p>To use a Jython module, it is necessary to create a .py file 122 located in a location where Jython can find it. The Jython 123 <code>sys.path</code> variable contains the Jython path. One way to 124 get the value of the sys.path variable is to enable debugging on the 125 actor by right clicking and selecting "Listen to Actor", which will 126 cause the preinitialize() method to print the contents of sys.path to 127 standard out. Another way to get the value of <code>sys.path</code> 128 is to run the Ptolemy model at 129 <code>ptolemy/actor/lib/python/test/PythonSysPath</code>. For 130 example, under Mac OS X for the ptII user, sys.path includes 131 <code>/Users/ptII/lib/Lib</code>. So, create that directory if 132 necessary and place the .py file in that directory, for example 133 <code>/Users/ptII/lib/Lib/PtPythonSquare.py</code></p> 134 135 <pre> 136class Main : 137 "Read the input and send the square to the output" 138 def fire(self) : 139 token = self.input.get(0) 140 self.output.broadcast(token.multiply(token)) 141 return 142 </pre> 143 144 <p>Then set <i>jythonClassName</i> to the name of the <b>Jython</b> 145 class, for example <code>PtPythonSquare.Main</code>. (Note that the 146 <i>jythonClassName</i> parameter should be set to the value of the 147 Jython class name before changing the <i>script</i> parameter to 148 import a Jython module.)</p> 149 150 <p>Then set <i>script</i> to to:</p> 151 <pre> 152 import PtPythonSquare 153 PtPythonSquare = reload(PtPythonSquare) 154 </pre> 155 156 157 <p>This class relies on <a href="http://jython.org">Jython</a>, which 158 is a Java implementation of Python. 159 160 <p>As of November, 2011 $PTII/lib/jython.jar was based on Jython 2.5.2.</p> 161 162 <p>See <a href="https://kepler-project.org/developers/reference/python-and-kepler#in_browser">Python and Kepler notes</a>.</p> 163 164 165 @author Xiaojun Liu 166 @version $Id$ 167 @since Ptolemy II 2.3 168 @Pt.ProposedRating Yellow (liuxj) 169 @Pt.AcceptedRating Red (reviewmoderator) 170 */ 171public class PythonScript extends TypedAtomicActor { 172 /** Construct an actor with the given container and name. 173 * In addition to invoking the base class constructor, 174 * create the <i>script</i> parameter, and initialize 175 * the script to provide an empty template. 176 * @param container The container. 177 * @param name The name of this actor. 178 * @exception NameDuplicationException If the container already 179 * has an actor with this name. 180 * @exception IllegalActionException If the actor cannot be contained 181 * by the proposed container. 182 */ 183 public PythonScript(CompositeEntity container, String name) 184 throws NameDuplicationException, IllegalActionException { 185 super(container, name); 186 187 jythonClassName = new StringAttribute(this, "jythonClassName"); 188 jythonClassName.setExpression("Main"); 189 190 script = new StringAttribute(this, "script"); 191 192 // Set the visibility to expert, as casual users should 193 // not see the script. This is particularly true if one 194 // installs an actor that is an instance of this with a 195 // particular script in the library. 196 script.setVisibility(Settable.EXPERT); 197 198 // initialize the script to provide an empty template: 199 // 200 // # This is a template. 201 // class Main : 202 // "description here" 203 // def fire(self) : 204 // # read input, compute, send output 205 // return 206 // 207 script.setExpression("# This is a template.\n" + "class Main :\n" 208 + " \"description here\"\n" + " def fire(self) :\n" 209 + " # Create ports, e.g. input and output.\n" 210 + " # Read input, for example using\n" 211 + " # token = self.input.get(0)\n" 212 + " # compute, and send an output using\n" 213 + " # self.output.broadcast(token)\n" + " return\n\n"); 214 _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-30\" y=\"-15\" " 215 + "width=\"60\" height=\"30\" " + "style=\"fill:black\"/>\n" 216 + "<text x=\"-22\" y=\"4\"" 217 + "style=\"font-size:14; fill:white; font-family:SansSerif\">" 218 + "Python</text>\n" + "</svg>\n"); 219 } 220 221 /////////////////////////////////////////////////////////////////// 222 //// ports and parameters //// 223 224 /** The Jython class name to be invoked. The default value is 225 * "Main", which indicates that the <i>script</i> parameter 226 * should define a class named "Main". If the <i>script</i> 227 * parameter imports a Jython module, for example: "import Foo", 228 * then Foo.py should define a class named "Main" and this 229 * parameter should have the value "Foo.Main". If the value of 230 * this parameter is anything other than "Main", then 231 * preinitialize() will reread the script. This is how Jython 232 * modules can be used. Note that the <i>jythonClassName</i> 233 * parameter should be set to the value of the Jython class name 234 * before changing the <i>script</i> parameter to import a Jython 235 * module. 236 */ 237 public StringAttribute jythonClassName; 238 239 /** The script that specifies the function of this actor. 240 * The default value is a script that copies the input to the output. 241 */ 242 public StringAttribute script; 243 244 /////////////////////////////////////////////////////////////////// 245 //// public methods //// 246 247 /** If <i>script</i> is changed, invoke the python interpreter to 248 * evaluate the script. 249 * @param attribute The attribute that changed. 250 * @exception IllegalActionException If there is any error in evaluating 251 * the script. 252 */ 253 @Override 254 public void attributeChanged(Attribute attribute) 255 throws IllegalActionException { 256 if (attribute == script) { 257 _evaluateScript(); 258 } else { 259 super.attributeChanged(attribute); 260 } 261 } 262 263 /** Clone the actor into the specified workspace. This calls the 264 * base class and then properly sets private variables. 265 * @param workspace The workspace for the new object. 266 * @return A new actor. 267 * @exception CloneNotSupportedException If a derived class contains 268 * an attribute that cannot be cloned. 269 */ 270 @Override 271 public Object clone(Workspace workspace) throws CloneNotSupportedException { 272 PythonScript newObject = (PythonScript) super.clone(workspace); 273 274 newObject._class = null; 275 newObject._methodMap = new HashMap(); 276 newObject._object = null; 277 return newObject; 278 } 279 280 /** Send the message to all registered debug listeners. In the script, 281 * use <code>self.actor.debug()</code> to call this method. 282 * @param message The debug message. 283 */ 284 public void debug(String message) { 285 if (_debugging) { 286 _debug("From script: ", message); 287 } 288 } 289 290 /** Invoke the fire() method if defined in the script. 291 * @exception IllegalActionException If there is any error in calling the 292 * fire() method defined by the script. 293 */ 294 @Override 295 public void fire() throws IllegalActionException { 296 super.fire(); 297 _invokeMethod("fire", null); 298 } 299 300 /** Invoke the initialize() method if defined in the script. 301 * @exception IllegalActionException If there is any error in calling the 302 * initialize() method defined by the script. 303 */ 304 @Override 305 public void initialize() throws IllegalActionException { 306 super.initialize(); 307 _invokeMethod("initialize", null); 308 } 309 310 /** Return true if this actor has at least one debug listener. 311 * @return True if this actor has at least one debug listener. 312 */ 313 public boolean isDebugging() { 314 return _debugging; 315 } 316 317 /** Invoke the postfire() method if defined in the script. Return true 318 * when the method return value is not zero, or the method does not 319 * return a value, or the method is not defined in the script. 320 * @return False if postfire() is defined in the script and returns 0, 321 * true otherwise. 322 * @exception IllegalActionException If there is any error in calling the 323 * postfire() method defined by the script. 324 */ 325 @Override 326 public boolean postfire() throws IllegalActionException { 327 boolean defaultResult = super.postfire(); 328 PyObject postfireResult = _invokeMethod("postfire", null); 329 330 if (postfireResult != null) { 331 return postfireResult.__nonzero__(); 332 } else { 333 return defaultResult; 334 } 335 } 336 337 /** Invoke the prefire() method if defined in the script. Return true 338 * when the method return value is not zero, or the method does not 339 * return a value, or the method is not defined in the script. 340 * @return False if prefire() is defined in the script and returns 0, 341 * true otherwise. 342 * @exception IllegalActionException If there is any error in calling the 343 * prefire() method. 344 */ 345 @Override 346 public boolean prefire() throws IllegalActionException { 347 boolean defaultResult = super.prefire(); 348 PyObject prefireResult = _invokeMethod("prefire", null); 349 350 if (prefireResult != null) { 351 return prefireResult.__nonzero__(); 352 } else { 353 return defaultResult; 354 } 355 } 356 357 /** Create an instance of the parameter named by the 358 * jythonClassName parameter that is defined in the script. Add 359 * all parameters and ports of this actor as attributes of the 360 * object, so that they become accessible to the methods defined 361 * in the script. 362 * @exception IllegalActionException If there is any error in 363 * creating an instance of the class named by the 364 * jythonClassName class defined in the script. 365 */ 366 @Override 367 public void preinitialize() throws IllegalActionException { 368 super.preinitialize(); 369 if (_debugging) { 370 _interpreter.exec("print sys.path"); 371 } 372 373 _object = _createObject(); 374 _invokeMethod("preinitialize", null); 375 } 376 377 /** Invoke the stop() method if defined in the script. Ignore any error 378 * in calling the method. 379 */ 380 @Override 381 public void stop() { 382 super.stop(); 383 384 try { 385 _invokeMethod("stop", null); 386 } catch (IllegalActionException e) { 387 if (_debugging) { 388 _debug(e.getMessage()); 389 } 390 } 391 } 392 393 /** Invoke the stopFire() method if defined in the script. Ignore any error 394 * in calling the method. 395 */ 396 @Override 397 public void stopFire() { 398 super.stopFire(); 399 400 try { 401 _invokeMethod("stopFire", null); 402 } catch (IllegalActionException e) { 403 if (_debugging) { 404 _debug(e.getMessage()); 405 } 406 } 407 } 408 409 /** Invoke the terminate() method if defined in the script. Ignore any 410 * error in calling the method. 411 */ 412 @Override 413 public void terminate() { 414 super.terminate(); 415 416 try { 417 _invokeMethod("terminate", null); 418 } catch (IllegalActionException e) { 419 if (_debugging) { 420 _debug(e.getMessage()); 421 } 422 } 423 } 424 425 /** Invoke the wrapup() method if defined in the script. Ignore any error 426 * in calling the method. 427 * @exception IllegalActionException If there is any error in calling the 428 * wrapup() method defined in the script. 429 */ 430 @Override 431 public void wrapup() throws IllegalActionException { 432 super.wrapup(); 433 _invokeMethod("wrapup", null); 434 } 435 436 /////////////////////////////////////////////////////////////////// 437 //// private methods //// 438 439 /** Create an instance of the class named by the jythonClassName 440 * parameter which is defined in the script. Add all parameters 441 * and ports of this actor as attributes of the object, so that 442 * they become accessible to the methods defined in the 443 * script. 444 * @exception IllegalActionException If there is any error in 445 * creating an instance of the class named by the jythonClassName 446 * parameter defined in the script. 447 */ 448 private PyObject _createObject() throws IllegalActionException { 449 // Create an instance by using the __call__ method 450 // of the class object 451 if (_class == null || !jythonClassName.getExpression().equals("Main")) { 452 // Since _class is null, we could have been cloned. 453 // Evaluate the script so that we do not use a different 454 // script of another python actor. (This will set _class). 455 _evaluateScript(); 456 } 457 PyObject object = _class.__call__(); 458 459 if (object == null) { 460 throw new IllegalActionException(this, 461 "Error in creating an instance of the " 462 + jythonClassName.getExpression() 463 + "defined in the script."); 464 } 465 466 // set up access to this actor 467 // first create an attribute "actor" on the object 468 // the PyObject class does not allow adding a new attribute to the 469 // object 470 object.__setattr__("actor", PyJavaType.wrapJavaObject(this)); 471 472 // give the object access to attributes and ports of this actor 473 Iterator attributes = attributeList().iterator(); 474 475 while (attributes.hasNext()) { 476 Attribute attribute = (Attribute) attributes.next(); 477 String mangledName = _mangleName(attribute.getName()); 478 479 if (_debugging) { 480 _debug("set up reference to attribute \"" + attribute.getName() 481 + "\" as \"" + mangledName + "\""); 482 } 483 484 object.__setattr__(new PyString(mangledName), 485 PyJavaType.wrapJavaObject(attribute)); 486 } 487 488 Iterator ports = portList().iterator(); 489 490 while (ports.hasNext()) { 491 Port port = (Port) ports.next(); 492 String mangledName = _mangleName(port.getName()); 493 494 if (_debugging) { 495 _debug("set up reference to port \"" + port.getName() 496 + "\" as \"" + mangledName + "\""); 497 } 498 499 object.__setattr__(new PyString(mangledName), 500 PyJavaType.wrapJavaObject(port)); 501 } 502 503 // populate the method map 504 for (int i = 0; i < _METHOD_NAMES.length; ++i) { 505 String methodName = _METHOD_NAMES[i]; 506 PyMethod method = null; 507 508 try { 509 method = (PyMethod) object.__findattr__(methodName); 510 } catch (ClassCastException ex) { 511 // the object has an attribute with the methodName but 512 // is not a method, ignore 513 } 514 515 _methodMap.put(methodName, method); 516 } 517 518 return object; 519 } 520 521 /** Evaluate the script by invoking the Jython interpreter. 522 * @exception IllegalActionException If the script does 523 * not define a class with the name of the value of the 524 * jythonClassName parameter, or the python interpreter cannot be 525 * initialized. 526 */ 527 private void _evaluateScript() throws IllegalActionException { 528 synchronized (_interpreter) { 529 String pythonScript = script.getExpression(); 530 531 try { 532 if (_debugging) { 533 _debug("PythonScript: evaluating " + pythonScript); 534 } 535 _interpreter.exec(pythonScript); 536 } catch (Exception ex) { 537 if (ex instanceof PyException) { 538 _reportScriptError((PyException) ex, 539 "Error in evaluating script:\n"); 540 } else { 541 throw new IllegalActionException(this, ex, 542 "Error in evaluating script:\n"); 543 } 544 } 545 546 // Get the class defined by the script. 547 try { 548 _class = (PyClass) _interpreter 549 .get(jythonClassName.getExpression()); 550 } catch (ClassCastException ex) { 551 try { 552 PyModule module = (PyModule) _interpreter 553 .get(jythonClassName.getExpression()); 554 _class = (PyClass) module.__findattr_ex__("Main"); 555 } catch (ClassCastException ex2) { 556 throw new IllegalActionException(this, ex, 557 "Failed to cast _interpreter.get(jythonClassName.getExpression()) " 558 + " which is of type " 559 + _interpreter 560 .get(jythonClassName 561 .getExpression()) 562 .getClass().getName() 563 + " to PyClass."); 564 } 565 } 566 567 if (_class == null) { 568 throw new IllegalActionException(this, 569 "The script does not define a \"" 570 + jythonClassName.getExpression() 571 + " \" class, try setting the jythonClassName parameter " 572 + "or have the script start with \"class " 573 + jythonClassName.getExpression() + "\"."); 574 } 575 } 576 } 577 578 /** Invoke the specified method on the instance of the class 579 * named by the jythonClassName parameter. Any argument that is 580 * not an instance of PyObject is wrapped in an instance of 581 * PyJavaType. The result of invoking the method is returned. 582 * @exception IllegalActionException If there is any 583 * error in calling the method. 584 */ 585 private PyObject _invokeMethod(String methodName, Object[] args) 586 throws IllegalActionException { 587 PyMethod method = (PyMethod) _methodMap.get(methodName); 588 PyObject returnValue = null; 589 590 if (method != null) { 591 try { 592 if (args == null || args.length == 0) { 593 try { 594 returnValue = method.__call__(); 595 } catch (Exception ex) { 596 // If the inner exception is TerminateProcessException, 597 // then get the exception and rethrow it. 598 if (ex instanceof PyException) { 599 PyException pyException = (PyException) ex; 600 Object exceptionValue = pyException.value 601 .__tojava__(Exception.class); 602 if (exceptionValue instanceof Exception) { 603 Exception innerException = (Exception) exceptionValue; 604 if (innerException instanceof TerminateProcessException) { 605 // Work around bug reported by 606 // Norbert Podhorszki 607 // See python/test/auto/PythonScalePN.xml 608 throw (TerminateProcessException) innerException; 609 } else { 610 throw ex; 611 } 612 } else { 613 // Test PythonScript-2.5 illustrates 614 // why we need this. 615 throw ex; 616 } 617 } else { 618 throw ex; 619 } 620 } 621 } else { 622 PyObject[] convertedArgs = new PyObject[args.length]; 623 624 for (int i = 0; i < args.length; ++i) { 625 if (!(args[i] instanceof PyObject)) { 626 convertedArgs[i] = PyJavaType 627 .wrapJavaObject(args[i]); 628 } else { 629 convertedArgs[i] = (PyObject) args[i]; 630 } 631 } 632 633 returnValue = _object.__call__(convertedArgs); 634 } 635 } catch (TerminateProcessException terminate) { 636 // Rethrow the terminate exception. 637 // See python/test/auto/PythonScalePN.xml 638 throw terminate; 639 } catch (Exception ex) { 640 String messagePrefix = "Error in invoking the " + methodName 641 + " method:\n"; 642 if (ex instanceof PyException) { 643 _reportScriptError((PyException) ex, messagePrefix); 644 } else { 645 throw new IllegalActionException(this, ex, messagePrefix); 646 } 647 } 648 } 649 650 return returnValue; 651 } 652 653 /* Mangle the given name (usually the name of an entity, or a parameter, 654 * or a port). Any character that is not legal in Java identifiers is 655 * changed to the underscore character. 656 */ 657 private String _mangleName(String name) { 658 char[] nameChars = name.toCharArray(); 659 boolean mangled = false; 660 661 for (int i = 0; i < nameChars.length; ++i) { 662 if (!Character.isJavaIdentifierPart(nameChars[i])) { 663 nameChars[i] = '_'; 664 mangled = true; 665 } 666 } 667 668 if (mangled) { 669 return new String(nameChars); 670 } 671 672 return name; 673 } 674 675 /* Report an error in evaluating the script or calling a method defined 676 * in the script. 677 */ 678 private void _reportScriptError(PyException ex, String messagePrefix) 679 throws IllegalActionException { 680 String message = ex.toString(); 681 int i = message.indexOf("line"); 682 683 if (i >= 0) { 684 message = message.substring(i); 685 } 686 687 throw new IllegalActionException(this, ex, messagePrefix + message); 688 } 689 690 /////////////////////////////////////////////////////////////////// 691 //// private variables //// 692 // The class defined in the script. 693 private PyClass _class; 694 695 // The python interpreter. 696 //private static PythonInterpreter _interpreter = new PythonInterpreter(); 697 private static PythonInterpreter _interpreter; 698 699 static { 700 try { 701 // If the python.home property is not set, then set it 702 // so that we can figure out where to write the jython cache. 703 // 704 // Under Webstart python/core/PySystemState.findRoot() first 705 // looks for the python.home property, so if it is 706 // not set we set it. 707 if (System.getProperty("python.home") == null) { 708 // Look for jython.jar in the classpath 709 // Start of code based on python/core/PySystemState.findRoot() 710 String classpath = StringUtilities 711 .getProperty("java.class.path"); 712 713 int jythonIndex = -1; 714 if (classpath == null) { 715 System.setProperty("python.home", 716 StringUtilities.getProperty("user.home")); 717 } else { 718 jythonIndex = classpath.toLowerCase(Locale.getDefault()) 719 .indexOf("jython.jar"); 720 } 721 722 if (jythonIndex == -1) { 723 // We did not find jython.jar, so set it to user.home. 724 // WebStart will end up here. 725 System.setProperty("python.home", 726 StringUtilities.getProperty("user.home")); 727 } else { 728 // We found jython.jar, return the parent directory. 729 // Under WebStart, jython.jar will not be in the classpath 730 int start = classpath.lastIndexOf( 731 java.io.File.pathSeparator, jythonIndex) + 1; 732 System.setProperty("python.home", 733 classpath.substring(start, jythonIndex)); 734 } 735 736 // End of code based on python/core/PySystemState.findRoot() 737 } 738 } catch (Exception ex) { 739 // Ignore, we are probably under an an applet 740 System.err.println( 741 "Warning: PythonScript threw an exception. Perhaps we are under an applet?"); 742 ex.printStackTrace(); 743 } 744 745 try { 746 _interpreter = new PythonInterpreter(); 747 } catch (java.security.AccessControlException ex) { 748 // In an applet, instantiating a PythonInterpreter 749 // causes PySystemState.initialize() to call 750 // System.getProperties(), which throws an exception 751 // The solution is to pass our own custom Properties 752 // Properties that are accessible via an applet. 753 String[] propertyNames = { "file.separator", "line.separator", 754 "path.separator", "java.class.version", "java.vendor", 755 "java.vendor.url", "java.version", "os.name", "os.arch", 756 "os.version" }; 757 Properties preProperties = new Properties(); 758 759 for (String propertyName : propertyNames) { 760 preProperties.setProperty(propertyName, 761 System.getProperty(propertyName)); 762 } 763 764 PySystemState.initialize(preProperties, null, new String[] { "" }); 765 _interpreter = new PythonInterpreter(); 766 } 767 768 try { 769 //String ptIIDir = StringUtilities.getProperty("ptolemy.ptII.dir"); 770 _interpreter.exec("import sys\n"); 771 //_interpreter.exec("sys.path.append('" + ptIIDir 772 // + "/ptolemy/actor/lib/python/test/')"); 773 774 } catch (Exception ex) { 775 ExceptionInInitializerError error = new ExceptionInInitializerError( 776 "The python command \"import sys\" failed."); 777 error.initCause(ex); 778 throw error; 779 } 780 781 String className = "ptolemy.kernel.util.NamedObj"; 782 String classResource = ClassUtilities.lookupClassAsResource(className); 783 784 if (classResource != null) { 785 //System.out.println("PythonScript: className: " + classResource); 786 File classFile = new File(classResource); 787 788 if (classFile.isDirectory()) { 789 PySystemState.add_extdir(classResource); 790 } else { 791 PySystemState.add_classdir(classResource); 792 } 793 } 794 } 795 796 // Map from method name to PyMethod objects. 797 private HashMap _methodMap = new HashMap(); 798 799 // The instance of the jythonClassName class defined in the script. 800 private PyObject _object; 801 802 // Invocation of methods named in this list is delegated to the instance 803 // of the jythonClassName class defined in the script. 804 // Listed here are all methods of the Executable interface, except 805 // iterate(). 806 private static final String[] _METHOD_NAMES = { "fire", "initialize", 807 "postfire", "prefire", "preinitialize", "stop", "stopFire", 808 "terminate", "wrapup" }; 809}