Accessing images from Python

My app structure is like this:
src/Products/my_products/...

For the Python 3 migration I switched from the unmaintained LocalFs (which made files on the file system available as objects) to using a resourceDirectory via zcml.

Access via browser works like a charm via eg. ++resource++assets/images/ClassIcons/SoftwareLicence.png, which maps the access to src/Products/assets/images/ClassIcons/SoftwareLicence.png.

The related zcml looks like this (abbreviated):

<browser:resourceDirectory
      name="assets"
      directory="static" />

Now I have to access these icons from my app via Python.

As assets is no Zope product, how would I access the images? I can't use an absolute path.

I have several working experiments but they all feel a bit odd.

a) using something like os.path.join(os.path.dirname(__file__), ...)

b) return pkg_resources.resource_filename( 'Products.assets', 'static/images/ClassIcons/' + self.className())

c) (not tried yet) Make the assets folder a Zope product?

Thank you for any tipps.

The ++ (as in your ++resource++assets) introduces a so called namespace (in your case with namespace type name resource and namespace name assets). Namespace lookup is supported by [un]restrictedTraverse. This implies, that you can e.g. use obj.unrestrictedTraverse("++resource++assets/images/ClassIcons/SoftwareLicence.png") (where obj is a Zope web object, typically your context object).

2 Likes

Thank you so much, Dieter!

I knew that there must be a better way!

Ok, it works, but I still have some question about what is the best way.

What I currently have...

path = os.path.join("++resource++assets/images/ClassIcons", self.className())
icon = self.unrestrictedTraverse(path)

As I need the binary data of the icon... I could either use...

icon.GET()

or

icon.context.data

or

with open(icon.context.path, 'rb') as f:
    f.read()

Is this actually "better" than using e.g. something like

        return pkg_resources.resource_filename(
            'Products.assets', 'static/images/ClassIcons/' + self.className())

When I use

path = os.path.join("++resource++assets/images/ClassIcons", self.className())
icon = self.unrestrictedTraverse(path)

icon is a ...

(Pdb++) icon
<Products.Five.browser.resource.FileResource object at 0x7fe973f04fd0>

This looks like there are quite some abstraction layers involved and also the Zope machinery.

Wouldn't pkg_resources be more efficient as it directly works on the file system?

Not that it really matters - as I have told from time to time, this is only an internal app for +- 50 users - I am just curios.

Thanks again!