thumbor / thumbor

thumbor is an open-source photo thumbnail service by globo.com

Home Page:http://thumbor.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unable to import local module for custom handlers

wfleming opened this issue · comments

Thumbor request URL

N/A

Expected behaviour

Should be able to import a local module for custom handlers, as documented at https://thumbor.readthedocs.io/en/stable/custom_handler_lists.html.

Actual behaviour

ModuleNotFoundError: No module named 'foo'
> thumbor --conf=./thumbor.conf
Traceback (most recent call last):
  File "/usr/local/bin/thumbor", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.11/site-packages/thumbor/server.py", line 158, in main
    importer = get_importer(config)
               ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/thumbor/server.py", line 63, in get_importer
    importer.import_modules()
  File "/usr/local/lib/python3.11/site-packages/thumbor/importer.py", line 90, in import_modules
    self.import_item("HANDLER_LISTS", is_multiple=True)
  File "/usr/local/lib/python3.11/site-packages/thumbor/importer.py", line 135, in import_item
    self.load_multiple_item(
  File "/usr/local/lib/python3.11/site-packages/thumbor/importer.py", line 172, in load_multiple_item
    module = self.import_class(
             ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/thumbor/importer.py", line 56, in import_class
    kls = import_class(name, get_module)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/thumbor/importer.py", line 20, in import_class
    module = import_module(module_name)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1206, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1178, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1142, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'foo'

Operating system

Debian 11 (python:3-slim docker image)

Your thumbor.conf

from thumbor.handler_lists import BUILTIN_HANDLERS
HANDLER_LISTS = BUILTIN_HANDLERS + ['foo']

(Not my real full .conf, of course, but reproduces the issue.)

foo.py in the current working directory (same directory as thumbor.conf)

from typing import Any, cast
from thumbor.handler_lists import HandlerList
from thumbor.handlers import BaseHandler

class IndexHandler(BaseHandler):
    async def get(self):
        self.set_status(302)
        self.set_header("Location", "https://www.example.com")

def get_handlers(_context: Any) -> HandlerList:
    return [ ("/", IndexHandler) ]

Additional details

Thumbor 7.5.0

python -c 'import foo' or import foo in a repl both work correctly, and I can call foo.get_handlers(None) in a repl and get the expected list.

I added some logging and pdb breakpoints locally, and inspecting the state at https://github.com/thumbor/thumbor/blob/master/thumbor/importer.py#L20 just before the failing import, I can see that os.getcwd() is still the directory I expect, where foo.py & thumbor.conf are. Oddly, the <class '_frozen_importlib_external.PathFinder'> entry in sys.meta_path at this point fails to find the module (sys.meta_path[i].find_module('foo') returns None, while in a python repl it successfully finds the module).

I presume that's the cause here, but I'm unclear on what about the execution environment changes between a plain python repl & the thumbor binary that is changing the import behavior. I am not setting PYTHONPATH or anything explicitly, and skimming through code I can't find anything in thumbor that seems to intentionally be changing the import paths. Is having a custom handler list in a local file like this intended to work, and is there some config I'm missing to make this work? Thanks for your help.

Circling back here: I found that setting PYTHONPATH to the directory of my thumbor config/module source fixed the issue. This surprised me a bit since docs didn't mention this as necessary and no env change was necessary to load the module from a repl. Not sure if this is expected behavior docs fail to mention or not. I'll leave this open since it seems like maybe there is at minimum a docs bug here, but if anyone else with this issue stumbles across this at least setting PYTHONPATH is a workaround.

Hi, this is a known Python behavior and is documented here: https://docs.python.org/3/reference/import.html#path-entry-finders