Skip to content

Commit

Permalink
Fix Windows support (#40)
Browse files Browse the repository at this point in the history
* Test on multiple platforms

* Don't install gunicorn on Windows

* Fall back on Flask when Gunicorn is missing

* Update changelog

* Remove travis config
  • Loading branch information
di authored May 8, 2020
1 parent f20e114 commit 901290d
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 84 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: CI

on: [push, pull_request]

jobs:

lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: Install tox
run: python -m pip install tox
- name: Run linting
run: python -m tox -e lint

test:
strategy:
matrix:
python: [3.6, 3.7, 3.8]
platform: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python }}
- name: Install tox
run: python -m pip install tox
- name: Run tests
run: python -m tox -e py-${{ matrix.platform }}
18 changes: 0 additions & 18 deletions .travis.yml

This file was deleted.

2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
- Fix Windows support ([#38])

## [1.4.0] - 2020-05-06
- Use gunicorn as a production HTTP server
Expand Down Expand Up @@ -50,6 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[1.0.1]: https://github.com/GoogleCloudPlatform/functions-framework-python/releases/tag/v1.0.1
[1.0.0]: https://github.com/GoogleCloudPlatform/functions-framework-python/releases/tag/v1.0.0

[#38]: https://github.com/GoogleCloudPlatform/functions-framework-python/pull/38
[#33]: https://github.com/GoogleCloudPlatform/functions-framework-python/pull/33
[#31]: https://github.com/GoogleCloudPlatform/functions-framework-python/pull/31
[#20]: https://github.com/GoogleCloudPlatform/functions-framework-python/pull/20
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"flask>=1.0,<2.0",
"click>=7.0,<8.0",
"watchdog>=0.10.0",
"gunicorn>=19.2.0,<21.0",
"gunicorn>=19.2.0,<21.0; platform_system!='Windows'",
],
extras_require={"test": ["pytest", "tox"]},
entry_points={
Expand Down
6 changes: 1 addition & 5 deletions src/functions_framework/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,5 @@ def _cli(target, source, signature_type, host, port, debug, dry_run):
click.echo("Function: {}".format(target))
click.echo("URL: http://{}:{}/".format(host, port))
click.echo("Dry run successful, shutting down.")
elif debug:
# Run with Flask's development WSGI server
app.run(host, port, debug)
else:
# Run with Gunicorn's production WSGI server
create_server(app).run(host, port)
create_server(app, debug).run(host, port)
39 changes: 39 additions & 0 deletions src/functions_framework/_http/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from functions_framework._http.flask import FlaskApplication


class HTTPServer:
def __init__(self, app, debug, **options):
self.app = app
self.options = options

if debug:
self.server_class = FlaskApplication
else:
try:
from functions_framework._http.gunicorn import GunicornApplication

self.server_class = GunicornApplication
except ImportError as e:
self.server_class = FlaskApplication

def run(self, host, port):
http_server = self.server_class(self.app, host, port, **self.options)
http_server.run()


def create_server(wsgi_app, debug, **options):
return HTTPServer(wsgi_app, debug, **options)
24 changes: 24 additions & 0 deletions src/functions_framework/_http/flask.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


class FlaskApplication:
def __init__(self, app, host, port, **options):
self.app = app
self.host = host
self.port = port
self.options = options

def run(self):
self.app.run(self.host, self.port, debug=True, **self.options)
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,3 @@ def load_config(self):

def load(self):
return self.app


class HTTPServer:
def __init__(self, app, server_class, **options):
self.app = app
self.server_class = server_class
self.options = options

def run(self, host, port):
http_server = self.server_class(self.app, host, port, **self.options)
http_server.run()


def create_server(wsgi_app, **options):
return HTTPServer(wsgi_app, server_class=GunicornApplication, **options)
24 changes: 5 additions & 19 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,90 +31,77 @@ def test_cli_no_arguments():


@pytest.mark.parametrize(
"args, env, create_app_calls, app_run_calls, wsgi_server_run_calls",
"args, env, create_app_calls, run_calls",
[
(
["--target", "foo"],
{},
[pretend.call("foo", None, "http")],
[],
[pretend.call("0.0.0.0", 8080)],
),
(
[],
{"FUNCTION_TARGET": "foo"},
[pretend.call("foo", None, "http")],
[],
[pretend.call("0.0.0.0", 8080)],
),
(
["--target", "foo", "--source", "/path/to/source.py"],
{},
[pretend.call("foo", "/path/to/source.py", "http")],
[],
[pretend.call("0.0.0.0", 8080)],
),
(
[],
{"FUNCTION_TARGET": "foo", "FUNCTION_SOURCE": "/path/to/source.py"},
[pretend.call("foo", "/path/to/source.py", "http")],
[],
[pretend.call("0.0.0.0", 8080)],
),
(
["--target", "foo", "--signature-type", "event"],
{},
[pretend.call("foo", None, "event")],
[],
[pretend.call("0.0.0.0", 8080)],
),
(
[],
{"FUNCTION_TARGET": "foo", "FUNCTION_SIGNATURE_TYPE": "event"},
[pretend.call("foo", None, "event")],
[],
[pretend.call("0.0.0.0", 8080)],
),
(
["--target", "foo", "--dry-run"],
{},
[pretend.call("foo", None, "http")],
[],
[],
),
(
[],
{"FUNCTION_TARGET": "foo", "DRY_RUN": "True"},
[pretend.call("foo", None, "http")],
[],
[],
),
(
["--target", "foo", "--host", "127.0.0.1"],
{},
[pretend.call("foo", None, "http")],
[],
[pretend.call("127.0.0.1", 8080)],
),
(
["--target", "foo", "--debug"],
{},
[pretend.call("foo", None, "http")],
[pretend.call("0.0.0.0", 8080, True)],
[],
[pretend.call("0.0.0.0", 8080)],
),
(
[],
{"FUNCTION_TARGET": "foo", "DEBUG": "True"},
[pretend.call("foo", None, "http")],
[pretend.call("0.0.0.0", 8080, True)],
[],
[pretend.call("0.0.0.0", 8080)],
),
],
)
def test_cli(
monkeypatch, args, env, create_app_calls, app_run_calls, wsgi_server_run_calls,
):
def test_cli(monkeypatch, args, env, create_app_calls, run_calls):
wsgi_server = pretend.stub(run=pretend.call_recorder(lambda *a, **kw: None))
wsgi_app = pretend.stub(run=pretend.call_recorder(lambda *a, **kw: None))
create_app = pretend.call_recorder(lambda *a, **kw: wsgi_app)
Expand All @@ -127,5 +114,4 @@ def test_cli(

assert result.exit_code == 0
assert create_app.calls == create_app_calls
assert wsgi_app.run.calls == app_run_calls
assert wsgi_server.run.calls == wsgi_server_run_calls
assert wsgi_server.run.calls == run_calls
Loading

0 comments on commit 901290d

Please sign in to comment.