def get_function_target(target): """Get the configured function target.""" target = target or os.environ.get("FUNCTION_TARGET", "") # Set the environment variable if it wasn't already os.environ["FUNCTION_TARGET"] = target if not target: raise InvalidConfigurationException( "Target is not specified (FUNCTION_TARGET environment variable not set)" ) return target
def create_app(target=None, source=None, signature_type=None): # Get the configured function target target = target or os.environ.get("FUNCTION_TARGET", "") # Set the environment variable if it wasn't already os.environ["FUNCTION_TARGET"] = target if not target: raise InvalidConfigurationException( "Target is not specified (FUNCTION_TARGET environment variable not set)" ) # Get the configured function source source = source or os.environ.get("FUNCTION_SOURCE", DEFAULT_SOURCE) # Python 3.5: os.path.exist does not support PosixPath source = str(source) # Set the template folder relative to the source path # Python 3.5: join does not support PosixPath template_folder = str(pathlib.Path(source).parent / "templates") if not os.path.exists(source): raise MissingSourceException( "File {source} that is expected to define function doesn't exist".format( source=source ) ) # Get the configured function signature type signature_type = signature_type or os.environ.get( "FUNCTION_SIGNATURE_TYPE", DEFAULT_SIGNATURE_TYPE ) # Set the environment variable if it wasn't already os.environ["FUNCTION_SIGNATURE_TYPE"] = signature_type # Load the source file: # 1. Extract the module name from the source path realpath = os.path.realpath(source) directory, filename = os.path.split(realpath) name, extension = os.path.splitext(filename) # 2. Create a new module spec = importlib.util.spec_from_file_location(name, realpath) source_module = importlib.util.module_from_spec(spec) # 3. Add the directory of the source to sys.path to allow the function to # load modules relative to its location sys.path.append(directory) # 4. Add the module to sys.modules sys.modules[name] = source_module # 5. Create the application app = flask.Flask(target, template_folder=template_folder) app.config["MAX_CONTENT_LENGTH"] = MAX_CONTENT_LENGTH app.register_error_handler(500, crash_handler) global errorhandler errorhandler = app.errorhandler # 6. Execute the module, within the application context with app.app_context(): spec.loader.exec_module(source_module) # Handle legacy GCF Python 3.7 behavior if os.environ.get("ENTRY_POINT"): os.environ["FUNCTION_TRIGGER_TYPE"] = signature_type os.environ["FUNCTION_NAME"] = os.environ.get("K_SERVICE", target) app.make_response_original = app.make_response def handle_none(rv): if rv is None: rv = "OK" return app.make_response_original(rv) app.make_response = handle_none # Handle log severity backwards compatibility import logging # isort:skip logging.info = _LoggingHandler("INFO", sys.stderr).write logging.warn = _LoggingHandler("ERROR", sys.stderr).write logging.warning = _LoggingHandler("ERROR", sys.stderr).write logging.error = _LoggingHandler("ERROR", sys.stderr).write logging.critical = _LoggingHandler("ERROR", sys.stderr).write sys.stdout = _LoggingHandler("INFO", sys.stderr) sys.stderr = _LoggingHandler("ERROR", sys.stderr) # Extract the target function from the source file if not hasattr(source_module, target): raise MissingTargetException( "File {source} is expected to contain a function named {target}".format( source=source, target=target ) ) function = getattr(source_module, target) # Check that it is a function if not isinstance(function, types.FunctionType): raise InvalidTargetTypeException( "The function defined in file {source} as {target} needs to be of " "type function. Got: invalid type {target_type}".format( source=source, target=target, target_type=type(function) ) ) # Mount the function at the root. Support GCF's default path behavior # Modify the url_map and view_functions directly here instead of using # add_url_rule in order to create endpoints that route all methods if signature_type == "http": app.url_map.add( werkzeug.routing.Rule("/", defaults={"path": ""}, endpoint="run") ) app.url_map.add(werkzeug.routing.Rule("/robots.txt", endpoint="error")) app.url_map.add(werkzeug.routing.Rule("/favicon.ico", endpoint="error")) app.url_map.add(werkzeug.routing.Rule("/<path:path>", endpoint="run")) app.view_functions["run"] = _http_view_func_wrapper(function, flask.request) app.view_functions["error"] = lambda: flask.abort(404, description="Not Found") app.after_request(read_request) elif signature_type == "event": app.url_map.add( werkzeug.routing.Rule( "/", defaults={"path": ""}, endpoint="run", methods=["POST"] ) ) app.url_map.add( werkzeug.routing.Rule("/<path:path>", endpoint="run", methods=["POST"]) ) app.view_functions["run"] = _event_view_func_wrapper(function, flask.request) # Add a dummy endpoint for GET / app.url_map.add(werkzeug.routing.Rule("/", endpoint="get", methods=["GET"])) app.view_functions["get"] = lambda: "" elif signature_type == "cloudevent": app.url_map.add( werkzeug.routing.Rule( "/", defaults={"path": ""}, endpoint=signature_type, methods=["POST"] ) ) app.url_map.add( werkzeug.routing.Rule( "/<path:path>", endpoint=signature_type, methods=["POST"] ) ) app.view_functions[signature_type] = _cloudevent_view_func_wrapper( function, flask.request ) else: raise FunctionsFrameworkException( "Invalid signature type: {signature_type}".format( signature_type=signature_type ) ) return app
def create_app(target=None, source=None, signature_type=None): # Get the configured function target target = target or os.environ.get("FUNCTION_TARGET", "") # Set the environment variable if it wasn't already os.environ["FUNCTION_TARGET"] = target if not target: raise InvalidConfigurationException( "Target is not specified (FUNCTION_TARGET environment variable not set)" ) # Get the configured function source source = source or os.environ.get("FUNCTION_SOURCE", DEFAULT_SOURCE) # Python 3.5: os.path.exist does not support PosixPath source = str(source) # Set the template folder relative to the source path # Python 3.5: join does not support PosixPath template_folder = str(pathlib.Path(source).parent / "templates") if not os.path.exists(source): raise MissingSourceException( "File {source} that is expected to define function doesn't exist". format(source=source)) # Get the configured function signature type signature_type = signature_type or os.environ.get( "FUNCTION_SIGNATURE_TYPE", DEFAULT_SIGNATURE_TYPE) # Set the environment variable if it wasn't already os.environ["FUNCTION_SIGNATURE_TYPE"] = signature_type # Load the source file spec = importlib.util.spec_from_file_location("main", source) source_module = importlib.util.module_from_spec(spec) sys.path.append(os.path.dirname(os.path.realpath(source))) spec.loader.exec_module(source_module) app = flask.Flask(target, template_folder=template_folder) # Extract the target function from the source file try: function = getattr(source_module, target) except AttributeError: raise MissingTargetException( "File {source} is expected to contain a function named {target}". format(source=source, target=target)) # Check that it is a function if not isinstance(function, types.FunctionType): raise InvalidTargetTypeException( "The function defined in file {source} as {target} needs to be of " "type function. Got: invalid type {target_type}".format( source=source, target=target, target_type=type(function))) # Mount the function at the root. Support GCF's default path behavior # Modify the url_map and view_functions directly here instead of using # add_url_rule in order to create endpoints that route all methods if signature_type == "http": app.url_map.add( werkzeug.routing.Rule("/", defaults={"path": ""}, endpoint="run")) app.url_map.add(werkzeug.routing.Rule("/<path:path>", endpoint="run")) app.view_functions["run"] = _http_view_func_wrapper( function, flask.request) elif signature_type == "event": app.url_map.add( werkzeug.routing.Rule("/", defaults={"path": ""}, endpoint="run", methods=["POST"])) app.url_map.add( werkzeug.routing.Rule("/<path:path>", endpoint="run", methods=["POST"])) app.view_functions["run"] = _event_view_func_wrapper( function, flask.request) # Add a dummy endpoint for GET / app.url_map.add( werkzeug.routing.Rule("/", endpoint="get", methods=["GET"])) app.view_functions["get"] = lambda: "" else: raise FunctionsFrameworkException( "Invalid signature type: {signature_type}".format( signature_type=signature_type)) return app
def create_app(target=None, source=None, signature_type=None): # Get the configured function target target = target or os.environ.get("FUNCTION_TARGET", "") # Set the environment variable if it wasn't already os.environ["FUNCTION_TARGET"] = target if not target: raise InvalidConfigurationException( "Target is not specified (FUNCTION_TARGET environment variable not set)" ) # Get the configured function source source = source or os.environ.get("FUNCTION_SOURCE", DEFAULT_SOURCE) # Python 3.5: os.path.exist does not support PosixPath source = str(source) # Set the template folder relative to the source path # Python 3.5: join does not support PosixPath template_folder = str(pathlib.Path(source).parent / "templates") if not os.path.exists(source): raise MissingSourceException( "File {source} that is expected to define function doesn't exist". format(source=source)) # Get the configured function signature type signature_type = signature_type or os.environ.get( "FUNCTION_SIGNATURE_TYPE", DEFAULT_SIGNATURE_TYPE) # Set the environment variable if it wasn't already os.environ["FUNCTION_SIGNATURE_TYPE"] = signature_type # Load the source file: # 1. Extract the module name from the source path realpath = os.path.realpath(source) directory, filename = os.path.split(realpath) name, extension = os.path.splitext(filename) # 2. Create a new module spec = importlib.util.spec_from_file_location(name, realpath) source_module = importlib.util.module_from_spec(spec) # 3. Add the directory of the source to sys.path to allow the function to # load modules relative to its location sys.path.append(directory) # 4. Add the module to sys.modules sys.modules[name] = source_module # 5. Execute the module spec.loader.exec_module(source_module) app = flask.Flask(target, template_folder=template_folder) app.config["MAX_CONTENT_LENGTH"] = MAX_CONTENT_LENGTH # Extract the target function from the source file try: function = getattr(source_module, target) except AttributeError: raise MissingTargetException( "File {source} is expected to contain a function named {target}". format(source=source, target=target)) # Check that it is a function if not isinstance(function, types.FunctionType): raise InvalidTargetTypeException( "The function defined in file {source} as {target} needs to be of " "type function. Got: invalid type {target_type}".format( source=source, target=target, target_type=type(function))) # Mount the function at the root. Support GCF's default path behavior # Modify the url_map and view_functions directly here instead of using # add_url_rule in order to create endpoints that route all methods if signature_type == "http": app.url_map.add( werkzeug.routing.Rule("/", defaults={"path": ""}, endpoint="run")) app.url_map.add(werkzeug.routing.Rule("/robots.txt", endpoint="error")) app.url_map.add(werkzeug.routing.Rule("/favicon.ico", endpoint="error")) app.url_map.add(werkzeug.routing.Rule("/<path:path>", endpoint="run")) app.view_functions["run"] = _http_view_func_wrapper( function, flask.request) app.view_functions["error"] = lambda: flask.abort( 404, description="Not Found") app.after_request(read_request) elif signature_type == "event" or signature_type == "cloudevent": app.url_map.add( werkzeug.routing.Rule("/", defaults={"path": ""}, endpoint=signature_type, methods=["POST"])) app.url_map.add( werkzeug.routing.Rule("/<path:path>", endpoint=signature_type, methods=["POST"])) # Add a dummy endpoint for GET / app.url_map.add( werkzeug.routing.Rule("/", endpoint="get", methods=["GET"])) app.view_functions["get"] = lambda: "" # Add the view functions app.view_functions["event"] = _event_view_func_wrapper( function, flask.request) app.view_functions["cloudevent"] = _cloudevent_view_func_wrapper( function, flask.request) else: raise FunctionsFrameworkException( "Invalid signature type: {signature_type}".format( signature_type=signature_type)) return app