Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] Function fails to load when another class file is in the function directory #1598

Open
jomalsan opened this issue Oct 11, 2024 · 3 comments

Comments

@jomalsan
Copy link

jomalsan commented Oct 11, 2024

Expected Behavior

I want to be able to split my logic into files within the function directory, or at least have it documented that I cannot.

Actual Behavior

When I added a second file to my function directory, if that file has a class the function will fail to load when deployed to an Azure Function App. Instead the logs will say:

2024-10-11T03:49:21Z   [Verbose]   Initiating background SyncTriggers operation
2024-10-11T03:49:21Z   [Information]   Loading functions metadata
2024-10-11T03:49:21Z   [Information]   Reading functions metadata (Custom)
2024-10-11T03:49:21Z   [Information]   1 functions found (Custom)
2024-10-11T03:49:21Z   [Information]   0 functions loaded
2024-10-11T03:49:21Z   [Information]   Loading functions metadata
2024-10-11T03:49:21Z   [Information]   Reading functions metadata (Custom)
2024-10-11T03:49:21Z   [Information]   1 functions found (Custom)
2024-10-11T03:49:21Z   [Information]   0 functions loaded

If that file has only function definitions than everything works as expected. I have also moved the helper file with a class definition to a sub directory and it worked again.

There appears to be some form of reflection that is preventing the function entry from being detected. If this is fundamental to how the worker functions, then adding clear guidance to the documentation that this can't be done would be helpful.

Steps to Reproduce

  1. Create a function app with the code below. This is pulled from the timer trigger documentation, but adds the import of the Helper class
  2. Add the following code to helpers.py in the function directory:
class Helper:
    def add(a: int, b: int) -> int:
        return a + b
  1. Deploy the function app. I used the VS Code extension
  2. Look at the logs of the Azure Function to see that the function never gets started. Logs included below

Relevant code being tried

import datetime
import logging
import azure.functions as func

from helpers import Helper

app = func.FunctionApp()


@app.function_name(name="my_timer")
@app.timer_trigger(schedule="0 */5 * * * *", arg_name="my_timer", run_on_startup=True)
def test_function(my_timer: func.TimerRequest) -> None:
    utc_timestamp = datetime.datetime.now()
    if my_timer.past_due:
        logging.info("The timer is past due!")
    logging.info("Python timer trigger function ran at %s", utc_timestamp)
    helper = Helper()
    logging.info(f"Result of add(1, 2): {helper.add(1, 2)}")

Relevant log output

...
2024-10-11T03:49:21Z   [Verbose]   Initiating background SyncTriggers operation
2024-10-11T03:49:21Z   [Information]   Loading functions metadata
2024-10-11T03:49:21Z   [Information]   Reading functions metadata (Custom)
2024-10-11T03:49:21Z   [Information]   1 functions found (Custom)
2024-10-11T03:49:21Z   [Information]   0 functions loaded
2024-10-11T03:49:21Z   [Information]   Loading functions metadata
2024-10-11T03:49:21Z   [Information]   Reading functions metadata (Custom)
2024-10-11T03:49:21Z   [Information]   1 functions found (Custom)
2024-10-11T03:49:21Z   [Information]   0 functions loaded
2024-10-11T03:50:01Z   [Verbose]   Received request to drain the host
2024-10-11T03:50:01Z   [Information]   DrainMode mode enabled
2024-10-11T03:50:01Z   [Information]   Calling StopAsync on the registered listeners
2024-10-11T03:50:01Z   [Information]   Call to StopAsync complete, registered listeners are now stopped
2024-10-11T03:50:01Z   [Verbose]   Received request to drain the host
2024-10-11T03:50:01Z   [Information]   DrainMode mode enabled
2024-10-11T03:50:01Z   [Information]   Calling StopAsync on the registered listeners
2024-10-11T03:50:01Z   [Information]   Call to StopAsync complete, registered listeners are now stopped
2024-10-11T03:51:06Z   [Verbose]   Received request to drain the host
2024-10-11T03:51:06Z   [Information]   DrainMode mode enabled
2024-10-11T03:51:06Z   [Information]   Calling StopAsync on the registered listeners
2024-10-11T03:51:06Z   [Information]   Call to StopAsync complete, registered listeners are now stopped
2024-10-11T03:51:06Z   [Verbose]   Received request to drain the host
2024-10-11T03:51:06Z   [Information]   DrainMode mode enabled
2024-10-11T03:51:06Z   [Information]   Calling StopAsync on the registered listeners
2024-10-11T03:51:06Z   [Information]   Call to StopAsync complete, registered listeners are now stopped
...

requirements.txt file

No response

Where are you facing this problem?

Production Environment (explain below)

Function app name

No response

Additional Information

No response

@jomalsan
Copy link
Author

Related #1315

@hallvictoria
Copy link
Contributor

Does this function also work and load when running locally?

I tried this as well, and it worked as expected.
function_app.py:

import logging
import azure.functions as func

from helpers import Helper

app = func.FunctionApp()

@app.timer_trigger(schedule="0 * * * * *", arg_name="myTimer", run_on_startup=False,
              use_monitor=False) 
def timer_trigger(myTimer: func.TimerRequest) -> None:
    if myTimer.past_due:
        logging.info('The timer is past due!')

    logging.info('Python timer trigger function executed.')
    #helper = Helper()
    logging.info(f"Result of add(1, 2): {Helper.add(1, 2)}")

helpers.py:

class Helper:
    def add(a: int, b: int) -> int:
        return a + b

directory structure:

- timer-trigger/
   - function_app.py 
   - helpers.py
   - ... 

One other way to make this work would be to create a subdirectory and add an init.py file, if it's easier to separate logic like that.

- timer-trigger/
   - function_app.py 
   - utils/
      - __init__.py
      - helpers.py 

@jomalsan
Copy link
Author

Thank you for your example! This helped me narrow in a bit further on what seems to be going on. If there is a runtime error in the code before the function is defined (missing import, exception thrown in loading), then the function does not get loaded and there is no error messaging displayed to the user. However, the function deploys successfully.

Is there any way to get clearer messaging that there was an error in the function code that is preventing it from being loaded? Potentially improving the log message

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants