Shared code cannot load xml.etree.ElementTree

I've written a Plex plug-in (MythRecordings) which works fine. So now I want to add a search service, and I want to share some code between the two.
 
I've read how this code can be placed in a Shared Code directory - and that works fine too, in a simple hello-world sort of way:
 
My shared code (which simply logs "TestIt called") goes in MythRecordings.Bundle/Contents/Services/Shared Code/plexmythlib.pys:
def TestIt():
   Log("TestIt called")
   return "Success!"

My channel code (which calls the shared code, using the SharedCodeService) goes in MythRecordings.Bundle/Contents/Code/__init__.py:

import xml.etree.ElementTree as ET
TestIt = SharedCodeService.plexmythlib.TestIt
def Start():
   TestIt()

And finally, my serach service goes in MythRecordings.Bundle/Contents/Services/Shared Code/plexmythlib.pys:

def Search(query):
   Log("Search service!")
   oc = ObjectContainer(title1='Myth Search service results', title2='Search Results')
   return oc

This works great! Looking in the plugin log file, I see

...
2015-02-26 20:25:27,762 (7fb64f093740) :  INFO (plexmythlib:14) - TestIt called
...

So at this point I can conclude that the shared code is located and registered correctly (I won't bore you with the details - it's all boilerplate).

 

HOWEVER, as soon as I update the shared code to reference xml.etree.ElementTree:

import xml.etree.ElementTree as ET
def TestIt():
   Log("TestIt called")
   return "Success!"

everything fails miserably - the log now goes:

...
2015-02-26 20:40:30,933 (7f11fffff700) :  DEBUG (services:362) - Loaded services
2015-02-26 20:40:30,939 (7f11ff7fe700) :  DEBUG (services:433) - Loading 1 shared code modules
2015-02-26 20:40:30,945 (7f11ff7fe700) :  CRITICAL (services:441) - Error loading shared code (most recent call last):
  File "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-ins/Framework.bundle/Contents/Resources/Versions/2/Python/Framework/components/services.py", line 435, in _setup_shared_code_sandbox
    sandbox.execute(code)
  File "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-ins/Framework.bundle/Contents/Resources/Versions/2/Python/Framework/code/sandbox.py", line 256, in execute
    exec(code) in self.environment
  File "", line 1, in 
  File "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-ins/Framework.bundle/Contents/Resources/Versions/2/Python/Framework/code/sandbox.py", line 333, in __import__
    return mod.load_module(_name)
  File "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-ins/Framework.bundle/Contents/Resources/Versions/2/Python/Framework/code/sandbox.py", line 44, in load_module
    module = RestrictedModule(name, path, sandbox)
  File "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-ins/Framework.bundle/Contents/Resources/Versions/2/Python/Framework/code/loader.py", line 30, in __init__
    exec(code) in self.__dict__
  File "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-ins/MythRecordings.bundle/Contents/Services/Shared Code/plexmythlib.pys", line 6, in 
    import xml.etree.ElementTree as ET
AttributeError: 'module' object has no attribute 'etree'
...

I can reference and use this library perfectly well from my main channel code (__init__.py), so it's in the Plex framework somewhere - may shared code just cannot see it.

 

Can someone tell me how I give the shared code access to the same modules as my __init__.py has?

 

PS: My apologies if this is common Python/Plex knowledge - I'm learning as I code....

 

I’m not sure but, do you really need to use xml directly? Wouldn’t be better to use Plex APIs instead which are already available without importing libraries?


You have XML, HTML, …, APIs


For example (though I’m not 100% sure):


def InternalGetRecordedListUnCached(maxCount = None):
url = PVR_URL + ‘Dvr/GetRecordedList’
if not maxCount is None:
url = url + “?Count=” + str(maxCount)
xmlstring = HTTP.Request(url, cacheTime = CACHE_TIME).content
root = ET.fromstring(xmlstring)
return root

looks the same as:

def InternalGetRecordedListUnCached(maxCount = None):
url = PVR_URL + ‘Dvr/GetRecordedList’
if not maxCount is None:
url = url + “?Count=” + str(maxCount)
root = XML.ElementFromURL(url, cacheTime = CACHE_TIME)
return root

Using those APIs with XPath to parse the trees is really powerful when you understand how it works.

@manfer

Good idea - I'm sure it'll be a potential performance optimization too.

The only reason I'm not marking this as solved yet is that this particular library is just an example - I'll probably run into the next example 2 seconds after implementing your (good!) idea.

So I'm still hoping someone out there will know why libraries (or modules, or whatever) that are available in __init__.py are not available in the shared code?

How to fix it would be nice to know too... :)

PS: If this had been a library I had installed (somehow) it would make sense - but this is a library that's just there for the picking (from __init__.py)

Could it be that you forgot to include the Elevated Policy in the plist of the Service?

I think of that because the error trace seems to indicate that the service code is running inside a sandbox.

This is just an idea. I'm not python expert and I don't know how Plex Core works in detail.

It is almost sure related to Plex Sandbox and the elevated policy. And I'm not even sure if you can get rid of the sandbox in services (it is possible there is no elevated policy for services). I would anyway try to stick to Plex APIs and let code run sandboxed if you can.