Repository: PyGithub/PyGithub

Issue:
Exception in get_dir_contents
In the latest release of PyGithub, I found the following exception thrown by get_dir_contents:

```
Traceback (most recent call last):
  File "/data/tyreunom/projects/offlate/offlate/systems/github.py", line 63, in send
    sha = project.get_dir_contents(mfile, 'translation').sha
  File "/gnu/store/jgz8y4a97rys726clj8n67kiqpwwy1bf-profile/lib/python3.7/site-packages/github/Repository.py", line 1806, in get_dir_contents
    for attributes in data
  File "/gnu/store/jgz8y4a97rys726clj8n67kiqpwwy1bf-profile/lib/python3.7/site-packages/github/Repository.py", line 1806, in <listcomp>
    for attributes in data
TypeError: string indices must be integers
```

I think the issue is that it expects to be looking for a directory, not a single file. Line 1806 has:

```python
return [
  github.ContentFile.ContentFile(..., completed=(attributes["type"] != "file"))
  for attributes in data
]
```

but data is a dictionary when looking for a single file, so attributes is a key (a string), leading to the above exception. I can work around that issue by looking for the parent directory and looping over the results.


Discussion:
I can reproduce that traceback when fetching a file, but not a directory. There is a more suitable method to fetch files: Repository.get_contents().
I'm tempted to "fix" this by throwing an exception if data is a dictionary.
oh I didn't see the get_contents, thanks. I guess it's fine to throw an exception, then. Would be nice to document that though, since they both use the same API call :)

Resolving patch:
diff --git a/github/Repository.py b/github/Repository.py
--- a/github/Repository.py
+++ b/github/Repository.py
@@ -91,6 +91,8 @@
 from base64 import b64encode
 import collections
 
+from deprecated import deprecated
+
 import github.GithubObject
 import github.PaginatedList
 import github.Invitation
@@ -1497,7 +1499,7 @@ def get_contents(self, path, ref=github.GithubObject.NotSet):
         :calls: `GET /repos/:owner/:repo/contents/:path <http://developer.github.com/v3/repos/contents>`_
         :param path: string
         :param ref: string
-        :rtype: :class:`github.ContentFile.ContentFile`
+        :rtype: :class:`github.ContentFile.ContentFile` or a list of them
         """
         assert isinstance(path, (str, six.text_type)), path
         assert ref is github.GithubObject.NotSet or isinstance(ref, (str, six.text_type)), ref
@@ -1512,9 +1514,20 @@ def get_contents(self, path, ref=github.GithubObject.NotSet):
             self.url + "/contents/" + six.moves.urllib.parse.quote(path),
             parameters=url_parameters
         )
+
+        # Handle 302 redirect response
+        if headers.get('status') == '302 Found' and headers.get('location'):
+            headers, data = self._requester.requestJsonAndCheck(
+                "GET",
+                headers['location'],
+                parameters=url_parameters
+            )
+
         if isinstance(data, list):
             return [
-                github.ContentFile.ContentFile(self._requester, headers, item, completed=False)
+                # Lazy completion only makes sense for files. See discussion
+                # here: https://github.com/jacquev6/PyGithub/issues/140#issuecomment-13481130
+                github.ContentFile.ContentFile(self._requester, headers, item, completed=(item["type"] != "file"))
                 for item in data
             ]
         return github.ContentFile.ContentFile(self._requester, headers, data, completed=True)
@@ -1775,6 +1788,10 @@ def delete_file(self, path, message, sha,
         return {'commit': github.Commit.Commit(self._requester, headers, data["commit"], completed=True),
                 'content': github.GithubObject.NotSet}
 
+    @deprecated(reason="""
+        Repository.get_dir_contents() is deprecated, use
+        Repository.get_contents() instead.
+        """)
     def get_dir_contents(self, path, ref=github.GithubObject.NotSet):
         """
         :calls: `GET /repos/:owner/:repo/contents/:path <http://developer.github.com/v3/repos/contents>`_
@@ -1782,29 +1799,7 @@ def get_dir_contents(self, path, ref=github.GithubObject.NotSet):
         :param ref: string
         :rtype: list of :class:`github.ContentFile.ContentFile`
         """
-        assert isinstance(path, (str, six.text_type)), path
-        assert ref is github.GithubObject.NotSet or isinstance(ref, (str, six.text_type)), ref
-        url_parameters = dict()
-        if ref is not github.GithubObject.NotSet:
-            url_parameters["ref"] = ref
-        headers, data = self._requester.requestJsonAndCheck(
-            "GET",
-            self.url + "/contents/" + six.moves.urllib.parse.quote(path),
-            parameters=url_parameters
-        )
-
-        # Handle 302 redirect response
-        if headers.get('status') == '302 Found' and headers.get('location'):
-            headers, data = self._requester.requestJsonAndCheck(
-                "GET",
-                headers['location'],
-                parameters=url_parameters
-            )
-
-        return [
-            github.ContentFile.ContentFile(self._requester, headers, attributes, completed=(attributes["type"] != "file"))  # Lazy completion only makes sense for files. See discussion here: https://github.com/jacquev6/PyGithub/issues/140#issuecomment-13481130
-            for attributes in data
-        ]
+        return self.get_contents(path, ref=ref)
 
     def get_contributors(self, anon=github.GithubObject.NotSet):
         """


Test patch: