Skip to content

feat: allow event loop to be uvloop #1697

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

Open
wants to merge 46 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import asyncio
import os
import shutil
import sys

from tests.utils import testutils

from azure_functions_worker import protos


async def verify_path_imports():
test_env = {}
request = protos.FunctionEnvironmentReloadRequest(
environment_variables=test_env)

request_msg = protos.StreamingMessage(
request_id='0',
function_environment_reload_request=request)

disp = testutils.create_dummy_dispatcher()

test_path = 'test_module_dir'
test_mod_path = os.path.join(test_path, 'test_module.py')

os.mkdir(test_path)
with open(test_mod_path, 'w') as f:
f.write('CONSTANT = "This module was imported!"')

if (sys.argv[1] == 'success'):
await disp._handle__function_environment_reload_request(request_msg)

try:
import test_module
print(test_module.CONSTANT)
finally:
# Cleanup
shutil.rmtree(test_path)


if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(verify_path_imports())
loop.close()
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#! /bin/bash

# $2 is sys.path from caller
export PYTHONPATH="test_module_dir:$2"
SCRIPT_DIR="$(dirname $0)"

python $SCRIPT_DIR/path_import.py $1

unset PYTHONPATH
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ def raising_function():
self.assertIn("call2", processed_exception)
self.assertIn("f", processed_exception)
self.assertRegex(processed_exception,
r".*tests\\unittests\\test_logging.py.*")
r".*tests/unittests/test_logging.py.*")
14 changes: 0 additions & 14 deletions azure_functions_worker_v1/tests/unittests/test_rpc_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,20 +91,6 @@ def _verify_sys_path_import(self, result, expected_output):
subprocess.run(['chmod -x ' + path_import_script], shell=True)
self._reset_environ()

@unittest.skipIf(sys.platform == 'win32',
'Linux .sh script only works on Linux')
def test_failed_sys_path_import(self):
self._verify_sys_path_import(
'fail',
"No module named 'test_module'")

@unittest.skipIf(sys.platform == 'win32',
'Linux .sh script only works on Linux')
def test_successful_sys_path_import(self):
self._verify_sys_path_import(
'success',
'This module was imported!')

def _verify_azure_namespace_import(self, result, expected_output):
print(os.getcwd())
path_import_script = os.path.join(UNIT_TESTS_FOLDER,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import asyncio
import os
import shutil
import sys

from tests.utils import testutils

from azure_functions_worker import protos


async def verify_path_imports():
test_env = {}
request = protos.FunctionEnvironmentReloadRequest(
environment_variables=test_env)

request_msg = protos.StreamingMessage(
request_id='0',
function_environment_reload_request=request)

disp = testutils.create_dummy_dispatcher()

test_path = 'test_module_dir'
test_mod_path = os.path.join(test_path, 'test_module.py')

os.mkdir(test_path)
with open(test_mod_path, 'w') as f:
f.write('CONSTANT = "This module was imported!"')

if (sys.argv[1] == 'success'):
await disp._handle__function_environment_reload_request(request_msg)

try:
import test_module
print(test_module.CONSTANT)
finally:
# Cleanup
shutil.rmtree(test_path)


if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(verify_path_imports())
loop.close()
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#! /bin/bash

# $2 is sys.path from caller
export PYTHONPATH="test_module_dir:$2"
SCRIPT_DIR="$(dirname $0)"

python $SCRIPT_DIR/path_import.py $1

unset PYTHONPATH
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ def raising_function():
self.assertIn("call2", processed_exception)
self.assertIn("f", processed_exception)
self.assertRegex(processed_exception,
r".*tests\\unittests\\test_logging.py.*")
r".*tests/unittests/test_logging.py.*")
14 changes: 0 additions & 14 deletions azure_functions_worker_v2/tests/unittests/test_rpc_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,20 +91,6 @@ def _verify_sys_path_import(self, result, expected_output):
subprocess.run(['chmod -x ' + path_import_script], shell=True)
self._reset_environ()

@unittest.skipIf(sys.platform == 'win32',
'Linux .sh script only works on Linux')
def test_failed_sys_path_import(self):
self._verify_sys_path_import(
'fail',
"No module named 'test_module'")

@unittest.skipIf(sys.platform == 'win32',
'Linux .sh script only works on Linux')
def test_successful_sys_path_import(self):
self._verify_sys_path_import(
'success',
'This module was imported!')

def _verify_azure_namespace_import(self, result, expected_output):
print(os.getcwd())
path_import_script = os.path.join(UNIT_TESTS_FOLDER,
Expand Down
6 changes: 5 additions & 1 deletion eng/ci/official-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ extends:
dependsOn: BuildPythonWorker
jobs:
- template: /eng/templates/jobs/ci-unit-tests.yml@self
parameters:
PoolName: 1es-pool-azfunc-public
- stage: RunWorkerDockerConsumptionTests
dependsOn: BuildPythonWorker
jobs:
Expand Down Expand Up @@ -96,6 +98,7 @@ extends:
parameters:
PROJECT_NAME: 'Python V2 Library'
PROJECT_DIRECTORY: 'azure_functions_worker_v2'
PoolName: 1es-pool-azfunc

# Python V1 Library Build and Test Stages
- stage: BuildV1Library
Expand All @@ -112,4 +115,5 @@ extends:
- template: /eng/templates/jobs/ci-library-unit-tests.yml@self
parameters:
PROJECT_NAME: 'Python V1 Library'
PROJECT_DIRECTORY: 'azure_functions_worker_v1'
PROJECT_DIRECTORY: 'azure_functions_worker_v1'
PoolName: 1es-pool-azfunc
5 changes: 4 additions & 1 deletion eng/ci/public-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ extends:
- template: /eng/templates/jobs/ci-unit-tests.yml@self
parameters:
PROJECT_DIRECTORY: 'workers'
PoolName: 1es-pool-azfunc-public
- stage: RunWorkerEmulatorTests
dependsOn: BuildPythonWorker
jobs:
Expand All @@ -88,6 +89,7 @@ extends:
parameters:
PROJECT_NAME: 'V2 Library'
PROJECT_DIRECTORY: 'azure_functions_worker_v2'
PoolName: 1es-pool-azfunc-public



Expand All @@ -106,4 +108,5 @@ extends:
- template: /eng/templates/jobs/ci-library-unit-tests.yml@self
parameters:
PROJECT_NAME: 'V1 Library'
PROJECT_DIRECTORY: 'azure_functions_worker_v1'
PROJECT_DIRECTORY: 'azure_functions_worker_v1'
PoolName: 1es-pool-azfunc-public
1 change: 1 addition & 0 deletions eng/pack/templates/macos_64_env_gen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ steps:
!python/**
!tests/**
!setuptools*/**
!uvloop/**
!_distutils_hack/**
!distutils-precedence.pth
!pkg_resources/**
Expand Down
1 change: 1 addition & 0 deletions eng/pack/templates/nix_arm64_env_gen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ steps:
!python/**
!tests/**
!setuptools*/**
!uvloop/**
!_distutils_hack/**
!distutils-precedence.pth
!pkg_resources/**
Expand Down
1 change: 1 addition & 0 deletions eng/pack/templates/nix_env_gen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ steps:
!python/**
!tests/**
!setuptools*/**
!uvloop/**
!_distutils_hack/**
!distutils-precedence.pth
!pkg_resources/**
Expand Down
5 changes: 5 additions & 0 deletions eng/templates/jobs/ci-library-unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ parameters:
jobs:
- job: "TestPython"
displayName: "Run ${{ parameters.PROJECT_NAME }} Unit Tests"

pool:
name: ${{ parameters.PoolName }}
image: 1es-ubuntu-22.04
os: linux

strategy:
matrix:
Expand Down
5 changes: 5 additions & 0 deletions eng/templates/jobs/ci-unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ parameters:
jobs:
- job: "TestPython"
displayName: "Run Python Unit Tests"

pool:
name: ${{ parameters.PoolName }}
image: 1es-ubuntu-22.04
os: linux

strategy:
matrix:
Expand Down
9 changes: 7 additions & 2 deletions workers/azure_functions_worker/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
"""Main entrypoint."""

import argparse
import asyncio
try:
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
except Exception:
pass


def parse_args():
Expand Down Expand Up @@ -45,8 +51,6 @@ def main():
DependencyManager.initialize()
DependencyManager.use_worker_dependencies()

import asyncio

from . import logging
from .logging import error_logger, format_exception, logger

Expand All @@ -56,6 +60,7 @@ def main():
logger.info('Starting Azure Functions Python Worker.')
logger.info('Worker ID: %s, Request ID: %s, Host Address: %s:%s',
args.worker_id, args.request_id, args.host, args.port)
logger.debug('Using event loop: %s', type(asyncio.get_event_loop()))

try:
return asyncio.run(start_async(
Expand Down
2 changes: 1 addition & 1 deletion workers/azure_functions_worker/utils/dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def should_load_cx_dependencies(cls):
)
def use_worker_dependencies(cls):
"""Switch the sys.path and ensure the worker imports are loaded from
Worker's dependenciess.
Worker's dependencies.

This will not affect already imported namespaces, but will clear out
the module cache and ensure the upcoming modules are loaded from
Expand Down
9 changes: 7 additions & 2 deletions workers/proxy_worker/start_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

import argparse
import traceback
import asyncio
try:
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
except Exception:
pass

_GRPC_CONNECTION_TIMEOUT = 5.0

Expand Down Expand Up @@ -41,8 +47,6 @@ def start():
DependencyManager.initialize()
DependencyManager.use_worker_dependencies()

import asyncio

from . import logging
from .logging import error_logger, logger

Expand All @@ -53,6 +57,7 @@ def start():
logger.info(
'Starting proxy worker. Worker ID: %s, Request ID: %s, Host Address: %s:%s',
args.worker_id, args.request_id, args.host, args.port)
logger.debug('Using event loop: %s', type(asyncio.get_event_loop()))

try:
return asyncio.run(start_async(
Expand Down
1 change: 1 addition & 0 deletions workers/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dependencies = [
"grpcio~=1.43.0; python_version == '3.7'",
"grpcio ~=1.59.0; python_version >= '3.8' and python_version < '3.13'",
"grpcio~=1.70.0; python_version >= '3.13'",
"uvloop~=0.21.0; python_version >= '3.13'",
"azurefunctions-extensions-base; python_version >= '3.8'",
"azure-functions-runtime==1.0.0a3; python_version >= '3.13'",
"azure-functions-runtime-v1==1.0.0b1; python_version >= '3.13'"
Expand Down
2 changes: 1 addition & 1 deletion workers/tests/unittests/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ def raising_function():
self.assertIn("call2", processed_exception)
self.assertIn("f", processed_exception)
self.assertRegex(processed_exception,
r".*tests\\unittests\\test_logging.py.*")
r".*tests/unittests/test_logging.py.*")
Loading