Skip to content

Commit

Permalink
empty-repository-load-config-error (#218)
Browse files Browse the repository at this point in the history
* Empty Repository Load Config Error
[release:0.27.8]

* style: format code with Black and isort

This commit fixes the style issues introduced in 0a73917 according to the output
from Black and isort.

Details: #218

* fix raise

* style: format code with Black, isort and Ruff Formatter

This commit fixes the style issues introduced in fdd79e7 according to the output
from Black, isort and Ruff Formatter.

Details: #218

* fixes

* style: format code with Black, isort and Ruff Formatter

This commit fixes the style issues introduced in f0d4a97 according to the output
from Black, isort and Ruff Formatter.

Details: #218

* some typing

* style: format code with Black, isort and Ruff Formatter

This commit fixes the style issues introduced in 200dc51 according to the output
from Black, isort and Ruff Formatter.

Details: #218

* docstrings

* style: format code with Black, isort and Ruff Formatter

This commit fixes the style issues introduced in cf3824d according to the output
from Black, isort and Ruff Formatter.

Details: #218

---------

Co-authored-by: Heitor Polidoro <[email protected]>
Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
  • Loading branch information
3 people committed May 1, 2024
1 parent 6cf10a2 commit c010f8e
Show file tree
Hide file tree
Showing 16 changed files with 152 additions and 33 deletions.
6 changes: 3 additions & 3 deletions .deepsource.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ version = 1
test_patterns = [
'**/tests/**',
'**/test_*.py',
'payload_helper.py'
]

[[analyzers]]
Expand All @@ -18,8 +17,9 @@ name = "python"

[[transformers]]
name = "black"
enabled = true

[[transformers]]
name = "isort"
enabled = true

[[transformers]]
name = "ruff"
30 changes: 22 additions & 8 deletions githubapp/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
"""

import os
import re
from collections.abc import Callable
from functools import wraps
from typing import Any, Callable, NoReturn, TypeVar, Union
from typing import Any, NoReturn, TypeVar, Union

import yaml
from github import UnknownObjectException
from github import GithubException, UnknownObjectException
from github.GithubObject import NotSet
from github.Repository import Repository

Expand Down Expand Up @@ -44,7 +44,9 @@ def set_values(self, data: dict[str, AnyBasic]) -> NoReturn:
else:
setattr(self, attr, value)

def create_config(self, name: str, *, default: AnyBasic = None, **values: AnyBasic) -> ConfigValueType:
def create_config(
self, name: str, *, default: AnyBasic = None, **values: AnyBasic
) -> ConfigValueType:
"""
Create a configuration value and nested values.
Expand All @@ -57,7 +59,9 @@ def create_config(self, name: str, *, default: AnyBasic = None, **values: AnyBas
ConfigValue: The created configuration value
"""
if default is not None and values:
raise ConfigError("You cannot set the default value AND default values for sub values")
raise ConfigError(
"You cannot set the default value AND default values for sub values"
)
default = default or ConfigValue()
if values:
default.set_values(values)
Expand All @@ -69,16 +73,26 @@ def load_config_from_file(self, filename: str, repository: Repository) -> NoRetu
"""Load the config from a file"""
try:
raw_data = (
yaml.safe_load(repository.get_contents(filename, ref=repository.default_branch).decoded_content) or {}
yaml.safe_load(
repository.get_contents(
filename, ref=repository.default_branch
).decoded_content
)
or {}
)
self.set_values(raw_data)
except UnknownObjectException:
pass
except GithubException as ghe:
if ghe.data.get("message") != "This repository is empty.":
raise

def __getattr__(self, item: str):
def __getattr__(self, item: str) -> Any:
if item.isupper():
return os.getenv(item)
raise ConfigError(f"No such config value for {item}. And there is no default value for it")
raise ConfigError(
f"No such config value for {item}. And there is no default value for it"
)

@staticmethod
def call_if(
Expand Down
2 changes: 2 additions & 0 deletions githubapp/event_check_run.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Class to represents an Event Check Run, a wrapper to Github CheckRun"""

from typing import Optional

from github.CheckRun import CheckRun
Expand Down
2 changes: 2 additions & 0 deletions githubapp/events/create.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Class to represents the Github Create events"""

from githubapp.events.event import Event


Expand Down
15 changes: 12 additions & 3 deletions githubapp/events/event.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Parent class to represents the Github events"""

import re
from typing import Any, Optional, TypeVar

Expand Down Expand Up @@ -33,8 +35,12 @@ def __init__(self, *, gh, requester, headers, sender, repository=None, **kwargs)
Event.delivery = headers["X-Github-Delivery"]
Event.github_event = headers["X-Github-Event"]
Event.hook_id = int(headers["X-Github-Hook-Id"])
Event.hook_installation_target_id = int(headers["X-Github-Hook-Installation-Target-Id"])
Event.hook_installation_target_type = headers["X-Github-Hook-Installation-Target-Type"]
Event.hook_installation_target_id = int(
headers["X-Github-Hook-Installation-Target-Id"]
)
Event.hook_installation_target_type = headers[
"X-Github-Hook-Installation-Target-Type"
]
if installation_id := kwargs.get("installation", {}).get("id"):
installation_id = int(installation_id)
Event.installation_id = installation_id
Expand Down Expand Up @@ -95,7 +101,10 @@ def match(cls, data):
Returns:
bool: True if the event matches the event_identifier, False otherwise
"""
return all((attr in data and value == data[attr]) for attr, value in cls.event_identifier.items())
return all(
(attr in data and value == data[attr])
for attr, value in cls.event_identifier.items()
)

@staticmethod
def fix_attributes(attributes):
Expand Down
2 changes: 2 additions & 0 deletions githubapp/events/issue_comment.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Class to represents the Github Issue Comment events"""

from github.Issue import Issue
from github.IssueComment import IssueComment

Expand Down
12 changes: 10 additions & 2 deletions githubapp/events/issues.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Class to represents the Github Issues events"""

from github.Issue import Issue
from github.Repository import Repository

Expand Down Expand Up @@ -29,8 +31,14 @@ def __init__(
**kwargs,
):
super().__init__(**kwargs)
self.old_issue = self._parse_object(Issue, changes.get("old_issue")) if changes else None
self.old_repository = self._parse_object(Repository, changes.get("old_repository")) if changes else None
self.old_issue = (
self._parse_object(Issue, changes.get("old_issue")) if changes else None
)
self.old_repository = (
self._parse_object(Repository, changes.get("old_repository"))
if changes
else None
)


class IssueEditedEvent(IssuesEvent):
Expand Down
2 changes: 2 additions & 0 deletions githubapp/events/pull_request_review.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Class to represents the Github Pull Request Review events"""

from github.PullRequest import PullRequest
from github.PullRequestReview import PullRequestReview

Expand Down
2 changes: 2 additions & 0 deletions githubapp/events/push.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Class to represents the Github Push events"""

from typing import Optional

from github.GitCommit import GitCommit
Expand Down
2 changes: 2 additions & 0 deletions githubapp/events/release.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Class to represents the Github Release events"""

from github.GitRelease import GitRelease

from githubapp.events.event import Event
Expand Down
2 changes: 2 additions & 0 deletions githubapp/events/status.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Class to represents the Github Status events"""

from typing import Optional

from github.Branch import Branch
Expand Down
35 changes: 28 additions & 7 deletions githubapp/webhook_handler.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Module to helps handle the Github webhooks events"""

import inspect
import os
import traceback
Expand Down Expand Up @@ -58,7 +60,9 @@ def decorator(handler: Callable[[Event], None]) -> Callable[[Event], None]:
return decorator


def register_method_for_event(event: type[Event], handler: Callable[[Event], None]) -> None:
def register_method_for_event(
event: type[Event], handler: Callable[[Event], None]
) -> None:
"""Add a handler for a specific event type.
The handler must accept only one argument of the Event type.
Expand All @@ -75,7 +79,9 @@ def register_method_for_event(event: type[Event], handler: Callable[[Event], Non
handlers[event].append(handler)


def _get_auth(hook_installation_target_id: int = None, installation_id: int = None) -> Auth:
def _get_auth(
hook_installation_target_id: int = None, installation_id: int = None
) -> Auth:
"""This method is used to get the authentication object for the GitHub API.
It checks if the environment variables CLIENT_ID, CLIENT_SECRET, and TOKEN are set.
If they are set, it uses the AppUserAuth object with the CLIENT_ID, CLIENT_SECRET, and TOKEN.
Expand All @@ -102,7 +108,9 @@ def _get_auth(hook_installation_target_id: int = None, installation_id: int = No
return Token(token)


def handle(headers: dict[str, Any], body: dict[str, Any], config_file: str = None) -> None:
def handle(
headers: dict[str, Any], body: dict[str, Any], config_file: str = None
) -> None:
"""Handle a webhook request.
The request headers and body are passed to the appropriate handler methods.
Expand Down Expand Up @@ -143,7 +151,9 @@ def handle(headers: dict[str, Any], body: dict[str, Any], config_file: str = Non
raise


def default_index(name, version=None, versions_to_show: Optional[list] = None) -> Callable[[], str]:
def default_index(
name: str, version: str = None, versions_to_show: Optional[list] = None
) -> Callable[[], str]:
"""Decorator to register a default root handler.
Args:
Expand All @@ -166,7 +176,14 @@ def root_wrapper() -> str:
"""
resp = f"<h1>{name} App up and running!</h1>"
if versions_to_show_:
resp = resp + "\n" + "<br>".join(f"{name_}: {version_}" for name_, version_ in versions_to_show_.items())
resp = (
resp
+ "\n"
+ "<br>".join(
f"{name_}: {version_}"
for name_, version_ in versions_to_show_.items()
)
)
return resp

return wraps(root_wrapper)(root_wrapper)
Expand Down Expand Up @@ -223,7 +240,9 @@ def handle_with_flask(
raise TypeError("app must be a Flask instance")

if use_default_index:
app.route("/", methods=["GET"])(default_index(app.name, version=version, versions_to_show=versions_to_show))
app.route("/", methods=["GET"])(
default_index(app.name, version=version, versions_to_show=versions_to_show)
)

@app.route(webhook_endpoint, methods=["POST"])
def webhook() -> str:
Expand Down Expand Up @@ -254,7 +273,9 @@ def auth_callback() -> str:
installation_id = int(args.get("installation_id"))
access_token = (
Github()
.get_oauth_application(os.getenv("CLIENT_ID"), os.getenv("CLIENT_SECRET"))
.get_oauth_application(
os.getenv("CLIENT_ID"), os.getenv("CLIENT_SECRET")
)
.get_access_token(code)
)

Expand Down
13 changes: 13 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,16 @@ relative_files = true

[tool.black]
line_length = 120

[tool.pylint]
max-line-length = 120
disable = [
"R0902",
"R0913",
"W0622",
"C0103",
"C0415",
]

[tool.mypy]
exclude = "tests/*"
32 changes: 29 additions & 3 deletions tests/test_config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from unittest.mock import Mock

import pytest
from github import UnknownObjectException
from github import GithubException, UnknownObjectException

from githubapp import Config
from githubapp.config import ConfigError
Expand Down Expand Up @@ -60,6 +60,26 @@ def test_config_on_file_not_found():
assert Config.config1 == "default1"


def test_config_on_empty_repository():
repository = Mock()
repository.get_contents.side_effect = GithubException(
404, data={"message": "This repository is empty."}
)
Config.load_config_from_file("file", repository)
Config.create_config("config1", default="default1")

assert Config.config1 == "default1"


def test_config_on_other_github_error():
repository = Mock()
repository.get_contents.side_effect = GithubException(
404, data={"message": "Other error"}
)
with pytest.raises(GithubException):
Config.load_config_from_file("file", repository)


def test_no_config_value():
repository = Mock()
repository.get_contents.return_value = Mock(decoded_content="")
Expand All @@ -68,13 +88,19 @@ def test_no_config_value():
with pytest.raises(ConfigError) as err:
# noinspection PyStatementEffect
Config.config1
assert str(err.value) == "No such config value for config1. And there is no default value for it"
assert (
str(err.value)
== "No such config value for config1. And there is no default value for it"
)


def test_validate_default_or_values():
with pytest.raises(ConfigError) as err:
Config.create_config("config1", default="value1", value2="value2")
assert str(err.value) == "You cannot set the default value AND default values for sub values"
assert (
str(err.value)
== "You cannot set the default value AND default values for sub values"
)


def test_config_call_if_call():
Expand Down
5 changes: 3 additions & 2 deletions tests/test_event.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from unittest.mock import Mock

from githubapp.events.event import Event
from tests.conftest import event_action_request
from tests.mocks import EventTest, SubEventTest


Expand Down Expand Up @@ -64,7 +63,9 @@ def test_parse_object():
self = Mock(requester="requester")
EventTest._parse_object(self, mocked_class, {"a": 1})
self.fix_attributes.assert_called_with({"a": 1})
mocked_class.assert_called_with(requester="requester", headers={}, attributes={"a": 1}, completed=False)
mocked_class.assert_called_with(
requester="requester", headers={}, attributes={"a": 1}, completed=False
)


# noinspection PyTypeChecker
Expand Down
Loading

0 comments on commit c010f8e

Please sign in to comment.