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

Made module exclusively Python3 #14

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 0 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
dist: xenial
language: python
python:
- "2.7"
- "3.6"
- "3.7"
- "pypy"
- "pypy3"
install:
- pip install tox-travis
Expand Down
16 changes: 15 additions & 1 deletion github_webhook/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,21 @@
:license: Apache License, Version 2.0
"""

from github_webhook.webhook import Webhook # noqa
from textwrap import dedent

import sys

if sys.version_info[0] < 3:
raise RuntimeError(
dedent(
"""Python runtime with major version >= 3 is required:
currently running on Python {version}""".format(
version=sys.version_info[0]
)
)
)

from github_webhook.webhook import Webhook

# -----------------------------------------------------------------------------
# Copyright 2015 Bloomberg Finance L.P.
Expand Down
11 changes: 6 additions & 5 deletions github_webhook/webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import hmac
import logging

import six
from flask import abort, request


Expand All @@ -21,7 +20,7 @@ def __init__(self, app, endpoint="/postreceive", secret=None):

self._hooks = collections.defaultdict(list)
self._logger = logging.getLogger("webhook")
if secret is not None and not isinstance(secret, six.binary_type):
if secret is not None and not isinstance(secret, bytes):
secret = secret.encode("utf-8")
self._secret = secret

Expand Down Expand Up @@ -51,8 +50,8 @@ def _postreceive(self):

if digest is not None:
sig_parts = _get_header("X-Hub-Signature").split("=", 1)
if not isinstance(digest, six.text_type):
digest = six.text_type(digest)
if not isinstance(digest, str):
digest = str(digest)

if len(sig_parts) < 2 or sig_parts[0] != "sha1" or not hmac.compare_digest(sig_parts[1], digest):
abort(400, "Invalid signature")
Expand All @@ -66,7 +65,9 @@ def _postreceive(self):
self._logger.info("%s (%s)", _format_event(event_type, data), _get_header("X-Github-Delivery"))

for hook in self._hooks.get(event_type, []):
hook(data)
resp = hook(data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the use case is sending an error, would this be better represented with an exception?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case we're returning the http code that the wrapped function returns. This could be any http code such as a 301 which is not necessarily an error. I think the current approach is the best choice here, but I'm happy to be corrected.

if resp: # Allow hook to respond if necessary
return resp

return "", 204

Expand Down
7 changes: 3 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

setup(
name="github-webhook",
version="1.0.2",
version="2.0.0",
description="Very simple, but powerful, microframework for writing Github webhooks in Python",
url="https://github.com/bloomberg/python-github-webhook",
author="Alex Chamberlain, Fred Phillips, Daniel Kiss, Daniel Beer",
author_email="[email protected], [email protected], [email protected], [email protected]",
license="Apache 2.0",
packages=["github_webhook"],
install_requires=["flask", "six"],
tests_require=["mock", "pytest"],
install_requires=["flask>=1.0.2"],
tests_require=["pytest"],
classifiers=[
"Development Status :: 4 - Beta",
"Framework :: Flask",
Expand All @@ -21,7 +21,6 @@
"Operating System :: MacOS :: MacOS X",
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 3",
"Topic :: Software Development :: Version Control",
],
Expand Down
160 changes: 160 additions & 0 deletions tests/test_webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import pytest
import werkzeug
from flask import Flask

try:
from unittest import mock
Expand Down Expand Up @@ -128,6 +129,165 @@ def test_request_had_headers(webhook, handler, mock_request):
webhook._postreceive()


# From https://developer.github.com/v3/activity/events/types/#pushevent
example_push_event = {
"ref": "refs/tags/simple-tag",
"before": "a10867b14bb761a232cd80139fbd4c0d33264240",
"after": "0000000000000000000000000000000000000000",
"created": False,
"deleted": True,
"forced": False,
"base_ref": None,
"compare": "https://github.com/Codertocat/Hello-World/compare/a10867b14bb7...000000000000",
"commits": [],
"head_commit": None,
"repository": {
"id": 135493233,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzU0OTMyMzM=",
"name": "Hello-World",
"full_name": "Codertocat/Hello-World",
"owner": {
"name": "Codertocat",
"email": "[email protected]",
"login": "Codertocat",
"id": 21031067,
"node_id": "MDQ6VXNlcjIxMDMxMDY3",
"avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/Codertocat",
"html_url": "https://github.com/Codertocat",
"followers_url": "https://api.github.com/users/Codertocat/followers",
"following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
"gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
"organizations_url": "https://api.github.com/users/Codertocat/orgs",
"repos_url": "https://api.github.com/users/Codertocat/repos",
"events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/Codertocat/received_events",
"type": "User",
"site_admin": False,
},
"private": False,
"html_url": "https://github.com/Codertocat/Hello-World",
"description": None,
"fork": False,
"url": "https://github.com/Codertocat/Hello-World",
"forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks",
"keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams",
"hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks",
"issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}",
"events_url": "https://api.github.com/repos/Codertocat/Hello-World/events",
"assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}",
"branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}",
"tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags",
"blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}",
"languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages",
"stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers",
"contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors",
"subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers",
"subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription",
"commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}",
"compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges",
"archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads",
"issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}",
"pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}",
"milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}",
"notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}",
"releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}",
"deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments",
"created_at": 1527711484,
"updated_at": "2018-05-30T20:18:35Z",
"pushed_at": 1527711528,
"git_url": "git://github.com/Codertocat/Hello-World.git",
"ssh_url": "[email protected]:Codertocat/Hello-World.git",
"clone_url": "https://github.com/Codertocat/Hello-World.git",
"svn_url": "https://github.com/Codertocat/Hello-World",
"homepage": None,
"size": 0,
"stargazers_count": 0,
"watchers_count": 0,
"language": None,
"has_issues": True,
"has_projects": True,
"has_downloads": True,
"has_wiki": True,
"has_pages": True,
"forks_count": 0,
"mirror_url": None,
"archived": False,
"open_issues_count": 2,
"license": None,
"forks": 0,
"open_issues": 2,
"watchers": 0,
"default_branch": "master",
"stargazers": 0,
"master_branch": "master",
},
"pusher": {"name": "Codertocat", "email": "[email protected]"},
"sender": {
"login": "Codertocat",
"id": 21031067,
"node_id": "MDQ6VXNlcjIxMDMxMDY3",
"avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/Codertocat",
"html_url": "https://github.com/Codertocat",
"followers_url": "https://api.github.com/users/Codertocat/followers",
"following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
"gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
"organizations_url": "https://api.github.com/users/Codertocat/orgs",
"repos_url": "https://api.github.com/users/Codertocat/repos",
"events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/Codertocat/received_events",
"type": "User",
"site_admin": False,
},
}


def test_push_request():
""" Uses the example event defined in the GitHub documentation to ensure
that our webhook app can receive the event.
"""

# GIVEN
app = Flask(__name__) # Standard Flask app
webhook = Webhook(app) # Defines '/postreceive' endpoint

@webhook.hook() # Defines a handler for the 'push' event
def on_push(data):
flag = data["repository"]["full_name"] == "Codertocat/Hello-World"
if not flag:
return "Event data does not match expected data", 400

# WHEN
resp = None
with app.test_client() as client:
resp = client.post(
"/postreceive", json=example_push_event, headers={"X-Github-Event": "push", "X-Github-Delivery": 0}
)

# THEN
assert resp.status_code == 204


# -----------------------------------------------------------------------------
# Copyright 2015 Bloomberg Finance L.P.
#
Expand Down
6 changes: 2 additions & 4 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
[tox]
envlist = py27,py36,py37,pypy,pypy3,flake8
envlist = py36,py37,pypy3,flake8

[testenv]
deps =
pytest
pytest-cov
flask
six
py{27,py}: mock
commands = pytest -vl --cov=github_webhook --cov-report term-missing --cov-fail-under 100
commands = pytest -vl --cov=github_webhook --cov-report term-missing --cov-fail-under 98

[testenv:flake8]
deps = flake8
Expand Down