Repository: brettcannon/gidgethub
Pull request #50 (2018-03-06 05:16:49)

Better handling of GitHub's 422 error.
If "errors" object was not provided, use the "message" field.

Fixes https://github.com/brettcannon/gidgethub/issues/49

Mariatta: Oh.. just my luck there's a new version of tornado released today v 5.0.0 likely breaking things :)

In my local env, if I pin the version to tornado==4.5.3 then the tests will pass...
brettcannon: Maybe https://pypi.org/project/pytest-tornado/ will work better? I have filed https://github.com/eukaryote/pytest-tornasync/issues/2 in case `pytest-tornasync` gets fixed.

Oh, and while we wait to see if anyone fixes the pytest fixtures, do you mind adding an entry to the changelog?
Mariatta: Thanks. I've added the changelog entry.

Tested locally,`pytest-tornado` is not any better. It is also hasn't been updated for 2 years. I'm guessing it is also not compatible with tornado 5.0.0.
brettcannon: @Mariatta if you need this ASAP we can push this through for now or figure out how to use http://www.tornadoweb.org/en/stable/testing.html . Otherwise let's give them a week or so before we start worrying about ripping out Tornado testing. 😉 
Mariatta: Thanks @brettcannon :) It's not too urgent for me, you can release when you're comfortable with it.
Mariatta: I can rebase after #51 is merged.
codecov[bot]: # [Codecov](https://codecov.io/gh/brettcannon/gidgethub/pull/50?src=pr&el=h1) Report
> Merging [#50](https://codecov.io/gh/brettcannon/gidgethub/pull/50?src=pr&el=desc) into [master](https://codecov.io/gh/brettcannon/gidgethub/commit/46ae0098861042de3c05b0bb466775b0394311e3?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/brettcannon/gidgethub/pull/50/graphs/tree.svg?width=650&height=150&src=pr&token=t1zJsj2sZH)](https://codecov.io/gh/brettcannon/gidgethub/pull/50?src=pr&el=tree)

```diff
@@          Coverage Diff          @@
##           master    #50   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files          14     14           
  Lines        1010   1023   +13     
  Branches       57     58    +1     
=====================================
+ Hits         1010   1023   +13
```


| [Impacted Files](https://codecov.io/gh/brettcannon/gidgethub/pull/50?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [gidgethub/sansio.py](https://codecov.io/gh/brettcannon/gidgethub/pull/50/diff?src=pr&el=tree#diff-Z2lkZ2V0aHViL3NhbnNpby5weQ==) | `100% <100%> (ø)` | :arrow_up: |
| [gidgethub/test/test\_sansio.py](https://codecov.io/gh/brettcannon/gidgethub/pull/50/diff?src=pr&el=tree#diff-Z2lkZ2V0aHViL3Rlc3QvdGVzdF9zYW5zaW8ucHk=) | `100% <100%> (ø)` | :arrow_up: |
| [gidgethub/test/test\_tornado.py](https://codecov.io/gh/brettcannon/gidgethub/pull/50/diff?src=pr&el=tree#diff-Z2lkZ2V0aHViL3Rlc3QvdGVzdF90b3JuYWRvLnB5) | `100% <0%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/brettcannon/gidgethub/pull/50?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/brettcannon/gidgethub/pull/50?src=pr&el=footer). Last update [46ae009...f5515dc](https://codecov.io/gh/brettcannon/gidgethub/pull/50?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

Mariatta: I've rebased.
brettcannon: Looks like the changelog entry disappeared. Add that and you can feel free to merge!
brettcannon: Actually, I forgot this is my personal repo, so I will merge when you add the entry. 😄 
Mariatta: Changelog updated.

Patch:
diff --git a/README.rst b/README.rst
index d0eb07b..24ca94e 100644
--- a/README.rst
+++ b/README.rst
@@ -68,6 +68,9 @@ Changelog
 
 - Provide a human-readable string representation of
   ``gidgethub.sansio.RateLimit``.
+  
+- Use the `message` data as the error message, if the `errors` object
+  was not returned.
 
 
 2.4.1
diff --git a/gidgethub/sansio.py b/gidgethub/sansio.py
index e342e88..b8b85ed 100644
--- a/gidgethub/sansio.py
+++ b/gidgethub/sansio.py
@@ -290,9 +290,12 @@ def decipher_response(status_code: int, headers: Mapping,
                 if not rate_limit.remaining:
                     raise RateLimitExceeded(rate_limit, message)
             elif status_code == 422:
-                errors = data["errors"]
-                fields = ", ".join(repr(e["field"]) for e in errors)
-                message = f"{message} for {fields}"
+                errors = data.get("errors", None)
+                if errors:
+                    fields = ", ".join(repr(e["field"]) for e in errors)
+                    message = f"{message} for {fields}"
+                else:
+                    message = data["message"]
                 raise InvalidField(errors, message)
         elif status_code >= 300:
             exc_type = RedirectionException


Test patch:
diff --git a/gidgethub/test/test_sansio.py b/gidgethub/test/test_sansio.py
index 3cd6ada..e38a4d7 100644
--- a/gidgethub/test/test_sansio.py
+++ b/gidgethub/test/test_sansio.py
@@ -2,7 +2,7 @@ import datetime
 import http
 import json
 import pathlib
-import urllib.parse
+
 
 import pytest
 
@@ -297,6 +297,17 @@ class TestDecipherResponse:
         assert exc_info.value.status_code == http.HTTPStatus(status_code)
         assert str(exc_info.value) == "it went bad for 'title'"
 
+    def test_422_no_errors_object(self):
+        status_code = 422
+        body = json.dumps({"message": "Reference does not exist",
+                           "documentation_url": "https://developer.github.com/v3/git/refs/#delete-a-reference"})
+        body = body.encode("utf-8")
+        headers = {"content-type": "application/json; charset=utf-8"}
+        with pytest.raises(InvalidField) as exc_info:
+            sansio.decipher_response(status_code, headers, body)
+        assert exc_info.value.status_code == http.HTTPStatus(status_code)
+        assert str(exc_info.value) == "Reference does not exist"
+
     def test_3XX(self):
         status_code = 301
         with pytest.raises(RedirectionException) as exc_info: