Import Python Module from Jar

Asked by Fagun

Hello All,

I am new to Python & Sikuli, but I have a little bit background of Java.

I am trying to automate an application using Sikuli which requires to have different sikuli script/modules which will be having Framework & reusable functions .

Now, We are looking for a way through which we can share this Framework/Common functions file to any other team member. All they can do is just import these to their script but they should not be able to modify it/have access to source code of that python module. Whole purpose is to have control of these Framework functions within core framework team.

Is there any way to do this?

After surfing, we got that distribution of python scripts to Jar using Jython can make it possible, but it never satisfied our goal. I am unable to import my sikuli script using jar.

Have followed below two links to do so-

http://doc.sikuli.org/globals.html#load
http://stackoverflow.com/questions/1252965/distributing-my-python-scripts-as-jars-with-jython

Please help.

Thanks in advance!

Question information

Language:
English Edit question
Status:
Solved
For:
SikuliX Edit question
Assignee:
No assignee Edit question
Solved by:
RaiMan
Solved:
Last query:
Last reply:
Revision history for this message
RaiMan (raimund-hocke) said :
#1

what version of Sikuli?

Revision history for this message
Best RaiMan (raimund-hocke) said :
#2

to help you, I need more information about the current organization/structure/content of your framework (not the code itself, only folder structure and how the stuff should be used).

the principal approach is
- to have a jar file with the same content as the folder, that would be used without the jar.
- have it at sys.path at runtime
- use import xyz

example:

-- folder1
   module1.py
   module2.py
   -- folder2 # multi-module
      __init__.py
      module3.py
      module4.py

after packing a jar, it's content should be

-- mystuff.jar
   module1.py
   module2.py
   -- folder2 # multi-module
      __init__.py
      module3.py
      module4.py

Be aware: it is not yet supported, to have .sikuli folders packed into the jar, only plain .py files. If you need images together with your code like in a .sikuli, you have to organise all images into a folder, that is packed to the jar as well and made available trough the image path feature at runtime.

the usage (principally):

sys.path.append("<path-to>/mystuff.jar")
import module1.py

the load() feature of SikuliX is a possible shortcut for the sys.path.append, but then the jar must be in special places depending on the Sikuli version.

Revision history for this message
Fagun (fagun-safi) said :
#4

Hey RaiMan,

Thank you so much for your help. This got worked. :)

I was using below commands before -
  sys.path.append("<path-to>")
  load("mystuff.jar")
  import module1.py

But after your explaination, I changed my code little bit -
  sys.path.append("<path-to>/mystuff.jar")
  import module1.py

----------------------------------------------------------

Further to it - I am using Sikuli-1.0.1, Sorry not to provide more specifications before.

Our Framewrok is NOT yet fully designed, but this is what we got so far -
1. Driver Script - Must be .Sikuli folder
2. Action Script - Must be .Py Files
3. Images Repository
4. Framework Functions - Must be .Py Files - Should be in JAR
5. Application Functions - Must be .Py Files

I still have one more doubt, that my JAR file (which has .PY files in it) are still NOT secure.
As I am using Zip-Unzip method to build a JAR.
How can I protect now? Any other way to satisfy our requirement?

Thanks Again! You are awesome.

Revision history for this message
RaiMan (raimund-hocke) said :
#5

ok, thanks for the feedback and additional information.

--- with version 1.0.1
- load() does not search on sys.path for the jar, so in your case you have to use:
load("<path-to>/mystuff.jar")
import module1.py

if you stick to the solution, you found, then use
if not "<path-to>" in sys.path: sys.path.append("<path-to>")

to avoid double entries (this is what load assures)

--- with version 1.1.0
Motivated by your request I revised the load() feature (the java docs):

  /**
   * add a jar to the scripting environment<br>
   * Jython: added to sys.path<br>
   * JRuby: not yet supported<br>
   * JavaScript: not yet supported<br>
   * if no scripting active (API usage), jar is added to classpath if available
   * @param fpJar absolute path to a jar (relative: searched according to Extension concept)
   * @return the absolute path to the jar or null, if not available
   */
  public static String load(String fpJar) {

  /**
   * add a jar to the scripting environment or to classpath<br>
   * Jython: added to sys.path<br>
   * JRuby: only added to classpath<br>
   * JavaScript: only added to classpath<br>
   * if no scripting is active (API usage), jar is added to classpath if available<br>
   * additionally: fpJar/fpJarImagePath is added to ImagePath (not checked)
   * @param fpJar absolute path to a jar (relative: searched according to Extension concept)
   * @param fpJarImagePath path relative to jar root inside jar
   * @return the absolute path to the jar or null, if not available
   */
  public static String load(String fpJar, String fpJarImagePath) {

... and I added support functions for your situation:

  /**
   * build a jar on the fly at runtime from a folder.<br>
   * special for Jython: if the folder contains a __init__.py on first level,
   * the folder will be copied to the jar root (hence preserving module folders)
   * @param targetJar absolute path to the created jar (parent folder must exist, jar is overwritten)
   * @param sourceFolder absolute path to a folder, the contained folder structure
   * will be copied to the jar root level
   * @return
   */
  public static boolean buildJarFromFolder(String targetJar, String sourceFolder) {

/**
   * the foo.py files in the given source folder are compiled to JVM-ByteCode-classfiles foo$py.class
   * and stored in the target folder (thus securing your code against changes).<br>
   * A folder structure is preserved. All files not ending as .py will be copied also.
   * The target folder might then be packed to a jar using buildJarFromFolder.<br>
   * Be aware: you will get no feedback about any compile problems,
   * so make sure your code compiles error free. Currently there is no support for running such a jar,
   * it can only be used with load()/import, but you might provide a simple script that does load()/import
   * and then runs something based on available functions in the jar code.
   * @param fpSource
   * @param fpTarget
   * @return false if anything goes wrong, true means should have worked
   */
  public static boolean compileJythonFolder(String fpSource, String fpTarget) {

the stuff is not ready yet, but will surely be in the next nightly after today.

In the scripting context, these features are available as
Sikulix.compileJythonFolder(source, target)

this compileJythonFolder feature would finally solve your should-not-be-changed-problem:

Sikulix.compileJythonFolder(source, target)
Sikulix. buildJarFromFolder(finalJar, target)

... and you have a finalJar, that does not contain .py files anymore, but their compiled versions ...$py.class, that cannot be modified.

Be aware: these features will not be available in version 1.0.1, but only in 1.1.0+
So I recommend to start testing with 1.1.0 as soon as possible and plan your productive environment with 1.1.0 final version (available during the next week)

Revision history for this message
RaiMan (raimund-hocke) said :
#6