Repository: PyGithub/PyGithub

Issue:
Creating new file in repo requires a leading /
If user calls the `create_file()` method of the `Repository` object and specifies a path to the file, and the path to the file does _not_ include a leading `/` character, the user gets an unhelpful "404 object not found" error, with no indication of which object can't be found (the file? the repo? the user?...)

[This Stack Overflow answer](https://stackoverflow.com/a/40614406/463213) mentions that you need a preceding `/` in the file path.

This is not mentioned anywhere in the documentation ([link to `create_file()` documentation](https://pygithub.readthedocs.io/en/latest/github_objects/Repository.html#github.Repository.Repository.create_file)). I tried to update the documentation but documentation is missing? See #845.

This issue didn't take me long to track down, but this has high, high potential to drive someone nuts if they don't have black belt google-fu skills. _Please_ include this crucial piece of information in the documentation (wherever it lives)!


Discussion:
Sorry if it's not clear. You just need to edit the python docs of the `create_file()` method, after it's merged to the master, the latest doc will be live on readthedocs.
Namely change this line:
https://github.com/PyGithub/PyGithub/blob/master/github/Repository.py#L1458

Resolving patch:
diff --git a/github/Repository.py b/github/Repository.py
--- a/github/Repository.py
+++ b/github/Repository.py
@@ -1449,7 +1449,7 @@ def get_file_contents(self, path, ref=github.GithubObject.NotSet):
             url_parameters["ref"] = ref
         headers, data = self._requester.requestJsonAndCheck(
             "GET",
-            self.url + "/contents" + urllib.quote(path),
+            self.url + "/contents/" + urllib.quote(path),
             parameters=url_parameters
         )
         if isinstance(data, list):
@@ -1530,7 +1530,7 @@ def create_file(self, path, message, content,
 
         headers, data = self._requester.requestJsonAndCheck(
             "PUT",
-            self.url + "/contents" + urllib.quote(path),
+            self.url + "/contents/" + urllib.quote(path),
             input=put_parameters
         )
 
@@ -1594,7 +1594,7 @@ def update_file(self, path, message, content, sha,
 
         headers, data = self._requester.requestJsonAndCheck(
             "PUT",
-            self.url + "/contents" + urllib.quote(path),
+            self.url + "/contents/" + urllib.quote(path),
             input=put_parameters
         )
 
@@ -1644,7 +1644,7 @@ def delete_file(self, path, message, sha,
 
         headers, data = self._requester.requestJsonAndCheck(
             "DELETE",
-            self.url + "/contents" + urllib.quote(path),
+            self.url + "/contents/" + urllib.quote(path),
             input=url_parameters
         )
 
@@ -1665,7 +1665,7 @@ def get_dir_contents(self, path, ref=github.GithubObject.NotSet):
             url_parameters["ref"] = ref
         headers, data = self._requester.requestJsonAndCheck(
             "GET",
-            self.url + "/contents" + urllib.quote(path),
+            self.url + "/contents/" + urllib.quote(path),
             parameters=url_parameters
         )
 


Test patch:
diff --git a/github/tests/Issue140.py b/github/tests/Issue140.py
--- a/github/tests/Issue140.py
+++ b/github/tests/Issue140.py
@@ -35,7 +35,7 @@ def setUp(self):
         self.repo = self.g.get_repo("twitter/bootstrap")
 
     def testGetDirContentsThenLazyCompletionOfFile(self):
-        contents = self.repo.get_dir_contents("/js")
+        contents = self.repo.get_dir_contents("js")
         self.assertEqual(len(contents), 15)
         n = 0
         for content in contents:
@@ -48,10 +48,10 @@ def testGetDirContentsThenLazyCompletionOfFile(self):
         self.assertEqual(n, 2)
 
     def testGetFileContents(self):
-        contents = self.repo.get_file_contents("/js/bootstrap-affix.js")
+        contents = self.repo.get_file_contents("js/bootstrap-affix.js")
         self.assertEqual(contents.encoding, "base64")
         self.assertEqual(contents.url, "https://api.github.com/repos/twitter/bootstrap/contents/js/bootstrap-affix.js")
         self.assertEqual(len(contents.content), 4722)
 
     def testGetDirContentsWithRef(self):
-        self.assertEqual(len(self.repo.get_dir_contents("/js", "8c7f9c66a7d12f47f50618ef420868fe836d0c33")), 15)
+        self.assertEqual(len(self.repo.get_dir_contents("js", "8c7f9c66a7d12f47f50618ef420868fe836d0c33")), 15)
diff --git a/github/tests/Issue174.py b/github/tests/Issue174.py
--- a/github/tests/Issue174.py
+++ b/github/tests/Issue174.py
@@ -35,5 +35,5 @@ def setUp(self):
         self.repo = self.g.get_repo("twitter/bootstrap")
 
     def testGetDirContentsWhithHttpRedirect(self):
-        contents = self.repo.get_dir_contents("/js/")
+        contents = self.repo.get_dir_contents("js/")
         self.assertEqual(len(contents), 15)
diff --git a/github/tests/ReplayData/ExposeAllAttributes.testAllClasses.txt b/github/tests/ReplayData/ExposeAllAttributes.testAllClasses.txt
--- a/github/tests/ReplayData/ExposeAllAttributes.testAllClasses.txt
+++ b/github/tests/ReplayData/ExposeAllAttributes.testAllClasses.txt
@@ -332,7 +332,7 @@ https
 GET
 api.github.com
 None
-/repos/jacquev6/PyGithub/contentsREADME.rst
+/repos/jacquev6/PyGithub/contents/README.rst
 {'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'}
 None
 200
diff --git a/github/tests/Repository.py b/github/tests/Repository.py
--- a/github/tests/Repository.py
+++ b/github/tests/Repository.py
@@ -513,20 +513,20 @@ def testAssignees(self):
 
     def testGetContents(self):
         self.assertEqual(len(self.repo.get_readme().content), 10212)
-        self.assertEqual(len(self.repo.get_contents("/doc/ReferenceOfClasses.md").content), 38121)
+        self.assertEqual(len(self.repo.get_contents("doc/ReferenceOfClasses.md").content), 38121)
 
     def testGetContentDir(self):
 
-        contents = self.repo.get_contents("/")
+        contents = self.repo.get_contents("")
         self.assertTrue(isinstance(contents, list))
         self.assertEquals(len(contents), 14)
 
     def testGetContentsWithRef(self):
         self.assertEqual(len(self.repo.get_readme(ref="refs/heads/topic/ExperimentOnDocumentation").content), 6747)
-        self.assertEqual(len(self.repo.get_contents("/doc/ReferenceOfClasses.md", ref="refs/heads/topic/ExperimentOnDocumentation").content), 43929)
+        self.assertEqual(len(self.repo.get_contents("doc/ReferenceOfClasses.md", ref="refs/heads/topic/ExperimentOnDocumentation").content), 43929)
 
     def testCreateFile(self):
-        newFile = '/doc/testCreateUpdateDeleteFile.md'
+        newFile = 'doc/testCreateUpdateDeleteFile.md'
         content = 'Hello world'
         self.repo.create_file(
             path=newFile, message='Create file for testCreateFile', content=content,
@@ -534,7 +534,7 @@ def testCreateFile(self):
             author=github.InputGitAuthor("Enix Yu", "enix223@163.com", "2016-01-15T16:13:30+12:00"))
 
     def testUpdateFile(self):
-        updateFile = '/doc/testCreateUpdateDeleteFile.md'
+        updateFile = 'doc/testCreateUpdateDeleteFile.md'
         content = 'Hello World'
         sha = self.repo.get_contents(updateFile).sha
         self.repo.update_file(
@@ -543,7 +543,7 @@ def testUpdateFile(self):
             author=github.InputGitAuthor("Enix Yu", "enix223@163.com", "2016-01-15T16:13:30+12:00"))
 
     def testDeleteFile(self):
-        deleteFile = '/doc/testCreateUpdateDeleteFile.md'
+        deleteFile = 'doc/testCreateUpdateDeleteFile.md'
         sha = self.repo.get_contents(deleteFile).sha
         self.repo.delete_file(path=deleteFile, message='Delete file for testDeleteFile', sha=sha, branch="master")