Repository: PyGithub/PyGithub

Issue:
Calling `Branch.edit_protection` fails when any `required_pull_request_reviews` field set
<strike>It seems that the signature for `Branch.edit_protection` differs in the `.pyi` and `.py` file. In the [`.pyi`](https://github.com/PyGithub/PyGithub/blob/094538e10c1da6f3bbd31ea1f2d102e48e3649c5/github/Branch.pyi#L38) file the arguments are called `user_bypass_pull_request_allowances`, `team_bypass_pull_request_allowances` and `app_bypass_pull_request_allowances` (not the leading singular) whereas in the [.py`](https://github.com/PyGithub/PyGithub/blob/094538e10c1da6f3bbd31ea1f2d102e48e3649c5/github/Branch.py#L131C4-L131C4) file the arguments are plural (`users_bypass_pull_request_allowances`, `teams_bypass_pull_request_allowances` and `apps_bypass_pull_request_allowances`)</strike> fixed in #2571.

There also seems to be a problem where passing nothing for `user_bypass_pull_request_allowances` results in an error when `require_code_owner_reviews` or `dismiss_stale_reviews` are given:

```
github.GithubException.GithubException: 422 {"message": "Invalid request.\n\nNo subschema in \"anyOf\" matched.\nFor 'properties/bypass_pull_request_allowances', nil is not an object.\nNot all subschemas of \"allOf\" matched.\nFor 'anyOf/1', {\"dismiss_stale_reviews\"=>true, \"require_code_owner_reviews\"=>true, \"bypass_pull_request_allowances\"=>nil} is not a null.", "documentation_url": "https://docs.github.com/rest/branches/branch-protection#update-branch-protection"}
```

https://docs.github.com/en/rest/branches/branch-protection?apiVersion=2022-11-28#update-branch-protection


Discussion:

Resolving patch:
diff --git a/github/Branch.py b/github/Branch.py
--- a/github/Branch.py
+++ b/github/Branch.py
@@ -267,54 +267,37 @@ def edit_protection(
                     "required_approving_review_count"
                 ] = required_approving_review_count
 
-            if (
-                dismissal_users is not github.GithubObject.NotSet
-                or dismissal_teams is not github.GithubObject.NotSet
-                or dismissal_apps is not github.GithubObject.NotSet
-            ):
-                post_parameters["required_pull_request_reviews"][
-                    "dismissal_restrictions"
-                ] = {}
-
+            dismissal_restrictions = {}
             if dismissal_users is not github.GithubObject.NotSet:
-                post_parameters["required_pull_request_reviews"][
-                    "dismissal_restrictions"
-                ]["users"] = dismissal_users
+                dismissal_restrictions["users"] = dismissal_users
             if dismissal_teams is not github.GithubObject.NotSet:
-                post_parameters["required_pull_request_reviews"][
-                    "dismissal_restrictions"
-                ]["teams"] = dismissal_teams
+                dismissal_restrictions["teams"] = dismissal_teams
             if dismissal_apps is not github.GithubObject.NotSet:
+                dismissal_restrictions["apps"] = dismissal_apps
+
+            if dismissal_restrictions:
                 post_parameters["required_pull_request_reviews"][
                     "dismissal_restrictions"
-                ]["apps"] = dismissal_apps
-
-            if (
-                users_bypass_pull_request_allowances is not github.GithubObject.NotSet
-                or teams_bypass_pull_request_allowances
-                is not github.GithubObject.NotSet
-                or apps_bypass_pull_request_allowances is not github.GithubObject.NotSet
-            ):
-                post_parameters["required_pull_request_reviews"][
-                    "bypass_pull_request_allowances"
-                ] = {}
-                if users_bypass_pull_request_allowances is github.GithubObject.NotSet:
-                    users_bypass_pull_request_allowances = []
-                if teams_bypass_pull_request_allowances is github.GithubObject.NotSet:
-                    teams_bypass_pull_request_allowances = []
-                if apps_bypass_pull_request_allowances is github.GithubObject.NotSet:
-                    apps_bypass_pull_request_allowances = []
-                post_parameters["required_pull_request_reviews"][
-                    "bypass_pull_request_allowances"
-                ] = {
-                    "users": users_bypass_pull_request_allowances,
-                    "teams": teams_bypass_pull_request_allowances,
-                    "apps": apps_bypass_pull_request_allowances,
-                }
-            else:
+                ] = dismissal_restrictions
+
+            bypass_pull_request_allowances = {}
+            if users_bypass_pull_request_allowances is not github.GithubObject.NotSet:
+                bypass_pull_request_allowances[
+                    "users"
+                ] = users_bypass_pull_request_allowances
+            if teams_bypass_pull_request_allowances is not github.GithubObject.NotSet:
+                bypass_pull_request_allowances[
+                    "teams"
+                ] = teams_bypass_pull_request_allowances
+            if apps_bypass_pull_request_allowances is not github.GithubObject.NotSet:
+                bypass_pull_request_allowances[
+                    "apps"
+                ] = apps_bypass_pull_request_allowances
+
+            if bypass_pull_request_allowances:
                 post_parameters["required_pull_request_reviews"][
                     "bypass_pull_request_allowances"
-                ] = None
+                ] = bypass_pull_request_allowances
         else:
             post_parameters["required_pull_request_reviews"] = None
         if (


Test patch:
diff --git a/tests/ReplayData/Branch.testEditProtection.txt b/tests/ReplayData/Branch.testEditProtection.txt
--- a/tests/ReplayData/Branch.testEditProtection.txt
+++ b/tests/ReplayData/Branch.testEditProtection.txt
@@ -4,7 +4,7 @@ api.github.com
 None
 /repos/jacquev6/PyGithub/branches/integrations/protection
 {'Authorization': 'Basic login_and_password_removed', 'Content-Type': 'application/json', 'User-Agent': 'PyGithub/Python', 'Accept': 'application/vnd.github.luke-cage-preview+json'}
-{"restrictions": null, "required_pull_request_reviews": {"bypass_pull_request_allowances": null, "require_code_owner_reviews": true, "required_approving_review_count": 2}, "required_status_checks": {"contexts": [], "strict": true}, "enforce_admins": null, "allow_force_pushes": null, "allow_fork_syncing": null, "lock_branch": null, "required_conversation_resolution": null, "required_linear_history": null, "block_creations": null }
+{"restrictions": null, "required_pull_request_reviews": {"require_code_owner_reviews": true, "required_approving_review_count": 2}, "required_status_checks": {"contexts": [], "strict": true}, "enforce_admins": null, "allow_force_pushes": null, "allow_fork_syncing": null, "lock_branch": null, "required_conversation_resolution": null, "required_linear_history": null, "block_creations": null }
 200
 [('status', '200 OK'), ('x-ratelimit-remaining', '4994'), ('content-length', '0'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"e0dc2dfc56971f4a36de1216356ea98b"'), ('date', 'Sat, 05 May 2018 06:05:54 GMT'), ('content-type', 'application/json; charset=utf-8')]
 ''
diff --git a/tests/ReplayData/Branch.testEditProtectionDismissalUsersWithUserOwnedBranch.txt b/tests/ReplayData/Branch.testEditProtectionDismissalUsersWithUserOwnedBranch.txt
--- a/tests/ReplayData/Branch.testEditProtectionDismissalUsersWithUserOwnedBranch.txt
+++ b/tests/ReplayData/Branch.testEditProtectionDismissalUsersWithUserOwnedBranch.txt
@@ -4,7 +4,7 @@ api.github.com
 None
 /repos/jacquev6/PyGithub/branches/integrations/protection
 {'Authorization': 'Basic login_and_password_removed', 'Content-Type': 'application/json', 'User-Agent': 'PyGithub/Python', 'Accept': 'application/vnd.github.luke-cage-preview+json'}
-{"restrictions": null, "required_pull_request_reviews": {"bypass_pull_request_allowances": null, "dismissal_restrictions": {"users": ["jacquev6"]}}, "required_status_checks": null, "enforce_admins": null, "allow_force_pushes": null, "allow_fork_syncing": null, "lock_branch": null, "required_conversation_resolution": null, "required_linear_history": null, "block_creations": null }
+{"restrictions": null, "required_pull_request_reviews": {"dismissal_restrictions": {"users": ["jacquev6"]}}, "required_status_checks": null, "enforce_admins": null, "allow_force_pushes": null, "allow_fork_syncing": null, "lock_branch": null, "required_conversation_resolution": null, "required_linear_history": null, "block_creations": null }
 422
 [('status', '422 Unprocessable Entity'), ('x-ratelimit-remaining', '4994'), ('content-length', '0'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"e0dc2dfc56971f4a36de1216356ea98b"'), ('date', 'Sun, 13 May 2018 10:42:14 GMT'), ('content-type', 'application/json; charset=utf-8')]
 {"documentation_url":"https://developer.github.com/v3/repos/branches/#update-branch-protection","message":"Validation Failed","errors":["Only organization repositories can have users and team restrictions"]}
diff --git a/tests/ReplayData/Branch.testEditProtectionPushRestrictionsAndDismissalUser.txt b/tests/ReplayData/Branch.testEditProtectionPushRestrictionsAndDismissalUser.txt
--- a/tests/ReplayData/Branch.testEditProtectionPushRestrictionsAndDismissalUser.txt
+++ b/tests/ReplayData/Branch.testEditProtectionPushRestrictionsAndDismissalUser.txt
@@ -4,7 +4,7 @@ api.github.com
 None
 /repos/PyGithub/PyGithub/branches/master/protection
 {'Authorization': 'Basic login_and_password_removed', 'Content-Type': 'application/json', 'User-Agent': 'PyGithub/Python', 'Accept': 'application/vnd.github.luke-cage-preview+json'}
-{"restrictions": {"apps": [], "teams": [], "users": ["jacquev6"]}, "required_pull_request_reviews": {"bypass_pull_request_allowances": null, "dismissal_restrictions": {"users": ["jacquev6"]}}, "required_status_checks": null, "enforce_admins": null, "allow_force_pushes": null, "allow_fork_syncing": null, "lock_branch": null, "required_conversation_resolution": null, "required_linear_history": null, "block_creations": null }
+{"restrictions": {"apps": [], "teams": [], "users": ["jacquev6"]}, "required_pull_request_reviews": {"dismissal_restrictions": {"users": ["jacquev6"]}}, "required_status_checks": null, "enforce_admins": null, "allow_force_pushes": null, "allow_fork_syncing": null, "lock_branch": null, "required_conversation_resolution": null, "required_linear_history": null, "block_creations": null }
 200
 [('status', '200 OK'), ('x-ratelimit-remaining', '4994'), ('content-length', '0'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"e0dc2dfc56971f4a36de1216356ea98b"'), ('date', 'Sun, 13 May 2018 13:21:24 GMT'), ('content-type', 'application/json; charset=utf-8')]
 ''