Skip to content

Commit

Permalink
feat: make function itself ASGI callable
Browse files Browse the repository at this point in the history
  • Loading branch information
lkingland committed Sep 27, 2024
1 parent 36392e0 commit 42a8efe
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 8 deletions.
26 changes: 20 additions & 6 deletions cmd/fhttp/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,28 @@
import logging
from func_python.http import serve

# Set the default logging level to INFO
logging.basicConfig(level=logging.INFO)

# Allow this test to be either instanced (default) or --static
# to test the two different primary method signatures supported in the
# final Function.
parser = argparse.ArgumentParser(description='Serve a Test Function')
parser.add_argument('--static', action='store_true',
help='Serve the example static handler (default is to '
'instantiate and serve the example class)')
args = parser.parse_args()


# Example static handler.
# Enable with --static
# Must be named exactly "handle"
async def handle(scope, receive, send):
""" handle is an example of a static handler which can be sent to the
middleware as a funciton. It will be wrapped in a default Funciton
instance before being served as an ASGI application.
"""
logging.info("OK: static")
logging.info("OK: static!!")

await send({
'type': 'http.response.start',
Expand All @@ -29,17 +36,19 @@ async def handle(scope, receive, send):
})


class Function:
# Example instanced handler
# This is the default expected by this test.
# The class can be named anything. See "new" below.
class MyFunction:
""" Function is an example of a functioon instance. The structure
implements the function which will be deployed as a network service.
The class name can be changed. The only required method is "handle".
"""

async def handle(self, scope, receive, send):
async def __call__(self, scope, receive, send):
""" handle is the only method which must be implemented by the
function instance for it to be served as an ASGI handler.
"""
logging.info("OK: instanced")
logging.info("OK: instanced!!")

await send({
'type': 'http.response.start',
Expand All @@ -52,16 +61,21 @@ async def handle(self, scope, receive, send):
})


# Funciton instance constructor
# expected to be named exactly "new"
# Must return a callable which conforms to the ASGI spec.
def new():
""" new is the factory function (or constructor) which will create
a new function instance when invoked. This must be named "new", and the
structure returned must include a method named "handle" which implements
the ASGI specification's method signature. The name of the class itself
can be changed.
"""
return Function()
return MyFunction()


# Run the example.
# Start either the static or instanced handler depending on flag --static
if __name__ == "__main__":
if args.static:
logging.info("Starting static handler")
Expand Down
8 changes: 6 additions & 2 deletions src/func_python/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ class DefaultFunction:
def __init__(self, handler):
self.handle = handler

async def __call__(self, scope, receive, send):
# delegate to the handler implementation provided during construction.
await self.handle (scope, receive, send)


class ASGIApplication():
def __init__(self, f):
Expand Down Expand Up @@ -105,8 +109,8 @@ async def __call__(self, scope, receive, send):
elif scope['path'] == '/health/readiness':
await self.handle_readiness(scope, receive, send)
else:
if hasattr(self.f, "handle"):
await self.f.handle(scope, receive, send)
if callable(self.f):
await self.f(scope, receive, send)
else:
raise Exception("function does not implement handle")
except Exception as e:
Expand Down

0 comments on commit 42a8efe

Please sign in to comment.