Posting to a Gist with Github API
Question:
I’ve been trying to write to a gist using Python urllib2
with the following:
def _log_error(information, date=datetime.date.today(), current_time=time.strftime("%H:%M:%S")):
log_string = """
Info: {}
Date: {}
Time: {}
""".format(information, date, current_time)
filename = "<file>"
token = "<token>"
access_url = "https://api.github.com/gists/{}".format(filename)
req = urllib2.Request(access_url)
req.add_header("Authorization", "token {}".format(token))
req.add_header("Content-Type", "application/json")
json_data = {"content": log_string}
urllib2.urlopen(req, data=json.dumps(json_data))
However, every time I try to do this, I get the following error:
Traceback (most recent call last):
File "printer.py", line 324, in <module>
_log_error("test")
File "printer.py", line 69, in _log_error
urllib2.urlopen(req, data=json.dumps(json_data))
File "C:Python27liburllib2.py", line 154, in urlopen
return opener.open(url, data, timeout)
File "C:Python27liburllib2.py", line 435, in open
response = meth(req, response)
File "C:Python27liburllib2.py", line 548, in http_response
'http', request, response, code, msg, hdrs)
File "C:Python27liburllib2.py", line 473, in error
return self._call_chain(*args)
File "C:Python27liburllib2.py", line 407, in _call_chain
result = func(*args)
File "C:Python27liburllib2.py", line 556, in http_error_default
raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
urllib2.HTTPError: HTTP Error 422: Unprocessable Entity
What is causing this error and how can I fix it, without using an external library (such as requests
)?
Answers:
To create a gist , use Create Gist endpoint requires the following JSON format :
{
"description": "the description for this gist",
"public": true,
"files": {
"file1.txt": {
"content": "String file contents"
}
}
}
The following will map the right fields for description
,public
,filename
and your 3 content fields info
,date
and current_time
:
import urllib2
import json
import datetime
import time
token = "YOUR_TOKEN"
access_url = "https://api.github.com/gists"
filename = "file.txt"
description = "the description for this gist"
public = "true"
information = "some info"
date = datetime.date.today()
current_time = time.strftime("%H:%M:%S")
data = """{
"description": "%s",
"public": %s,
"files": {
"%s": {
"content": "info : %s,date: %s, current_time: %s"
}
}
}"""
json_data = data % (description, public, filename, information, date, current_time)
req = urllib2.Request(access_url)
req.add_header("Authorization", "token {}".format(token))
req.add_header("Content-Type", "application/json")
urllib2.urlopen(req, data=json_data)
There is a Gist client I am maintaining that would do exactly what you need as well as what you asked for in the comments. You can clone it and install it via pip from here.
A brief summary of usage while creating and updating gists as you mentioned –
Create a gist
- Create interactively from an editor like nano, vim or gedit
gifc create create.md -d "How to create a gist from cli" -i nano
- Directly enter contents from cli
gifc create create.md -d "How to create a gist from cli" -m '''If you want to create a gist from an existing file then you do the following- `./gifc -c create.md -e "How to create a gist from cli" -i file.md`'''
- Take the contents from a file
gifc create create.md -d "How to create a gist from cli" -f file.md
Update a gist
-
Edit all (or some) files iteratively
gifc update ffd2f4a482684f56bf33c8726cc6ae63 -i vi
You can get the gist id from the get
method from earlier
-
Change description
gifc update ffd2f4a482684f56bf33c8726cc6ae63 -cd "New description"
You can get the gist id from the get
method from earlier
-
Edit contents of a file interactively in an editor like nano, vim or gedit
gifc update ffd2f4a482684f56bf33c8726cc6ae63 -f file_to_update.md
- Do both
gifc update ffd2f4a482684f56bf33c8726cc6ae63 -f file_to_update.md -cd "New description"
If you want to do this for Python3, I’ve updated Bertrand’s previous answer into a function.
"""Interact with Github Gists."""
import urllib
import json
def gist_of_it(token: str, payload: dict, gist_id: str = '', action: str = 'update'):
"""
Update or create a Github Gist.
Args:
token (str): The authorization token to be embededed in the request header.
Get a Github access token from settings > Developer Settings > Personal access tokens
payload (dict): A python dict for using in creating or updating the gist with format:
{
'description': 'The description for the gist',
'public': True or False,
'files': {
'the_filename.txt': {'content': 'Whatever the Gist file contents will be.'}
}
}
gist_id (str): The unique identifier of the gist to update. The gist_id can be found at the
end of your previosuly created Gist url. Not required when creating a new Gist. Defaults to ''.
action (str, optional): Specify to update and existing Gist or create a new one.
Values are either `'create'` or `'update'`. Defaults to 'update'.
Returns:
str: The json contents of the updated or new Gist page..
"""
if action not in ['create', 'update']:
raise ValueError('The action value must be either "create" or "update."')
access_url: str = 'https://api.github.com/gists'
# If you want to create a new gist file, do not append the gist_id to the access_url.
if action == 'update':
access_url = f'{access_url}/{gist_id}'
# Create json string to be uploaded and encode it.
json_data: bytes = json.dumps(payload).encode('utf-8')
headers: dict = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
}
# Post the data.
req: object = urllib.request.Request(access_url, json_data, headers)
with urllib.request.urlopen(req) as response:
gist_page = response.read() # The updated Gist page.
return gist_page
I’ve been trying to write to a gist using Python urllib2
with the following:
def _log_error(information, date=datetime.date.today(), current_time=time.strftime("%H:%M:%S")):
log_string = """
Info: {}
Date: {}
Time: {}
""".format(information, date, current_time)
filename = "<file>"
token = "<token>"
access_url = "https://api.github.com/gists/{}".format(filename)
req = urllib2.Request(access_url)
req.add_header("Authorization", "token {}".format(token))
req.add_header("Content-Type", "application/json")
json_data = {"content": log_string}
urllib2.urlopen(req, data=json.dumps(json_data))
However, every time I try to do this, I get the following error:
Traceback (most recent call last):
File "printer.py", line 324, in <module>
_log_error("test")
File "printer.py", line 69, in _log_error
urllib2.urlopen(req, data=json.dumps(json_data))
File "C:Python27liburllib2.py", line 154, in urlopen
return opener.open(url, data, timeout)
File "C:Python27liburllib2.py", line 435, in open
response = meth(req, response)
File "C:Python27liburllib2.py", line 548, in http_response
'http', request, response, code, msg, hdrs)
File "C:Python27liburllib2.py", line 473, in error
return self._call_chain(*args)
File "C:Python27liburllib2.py", line 407, in _call_chain
result = func(*args)
File "C:Python27liburllib2.py", line 556, in http_error_default
raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
urllib2.HTTPError: HTTP Error 422: Unprocessable Entity
What is causing this error and how can I fix it, without using an external library (such as requests
)?
To create a gist , use Create Gist endpoint requires the following JSON format :
{
"description": "the description for this gist",
"public": true,
"files": {
"file1.txt": {
"content": "String file contents"
}
}
}
The following will map the right fields for description
,public
,filename
and your 3 content fields info
,date
and current_time
:
import urllib2
import json
import datetime
import time
token = "YOUR_TOKEN"
access_url = "https://api.github.com/gists"
filename = "file.txt"
description = "the description for this gist"
public = "true"
information = "some info"
date = datetime.date.today()
current_time = time.strftime("%H:%M:%S")
data = """{
"description": "%s",
"public": %s,
"files": {
"%s": {
"content": "info : %s,date: %s, current_time: %s"
}
}
}"""
json_data = data % (description, public, filename, information, date, current_time)
req = urllib2.Request(access_url)
req.add_header("Authorization", "token {}".format(token))
req.add_header("Content-Type", "application/json")
urllib2.urlopen(req, data=json_data)
There is a Gist client I am maintaining that would do exactly what you need as well as what you asked for in the comments. You can clone it and install it via pip from here.
A brief summary of usage while creating and updating gists as you mentioned –
Create a gist
- Create interactively from an editor like nano, vim or gedit
gifc create create.md -d "How to create a gist from cli" -i nano
- Directly enter contents from cli
gifc create create.md -d "How to create a gist from cli" -m '''If you want to create a gist from an existing file then you do the following- `./gifc -c create.md -e "How to create a gist from cli" -i file.md`'''
- Take the contents from a file
gifc create create.md -d "How to create a gist from cli" -f file.md
Update a gist
-
Edit all (or some) files iteratively
gifc update ffd2f4a482684f56bf33c8726cc6ae63 -i vi
You can get the gist id from theget
method from earlier
-
Change description
gifc update ffd2f4a482684f56bf33c8726cc6ae63 -cd "New description"
You can get the gist id from theget
method from earlier
-
Edit contents of a file interactively in an editor like nano, vim or gedit
gifc update ffd2f4a482684f56bf33c8726cc6ae63 -f file_to_update.md
- Do both
gifc update ffd2f4a482684f56bf33c8726cc6ae63 -f file_to_update.md -cd "New description"
If you want to do this for Python3, I’ve updated Bertrand’s previous answer into a function.
"""Interact with Github Gists."""
import urllib
import json
def gist_of_it(token: str, payload: dict, gist_id: str = '', action: str = 'update'):
"""
Update or create a Github Gist.
Args:
token (str): The authorization token to be embededed in the request header.
Get a Github access token from settings > Developer Settings > Personal access tokens
payload (dict): A python dict for using in creating or updating the gist with format:
{
'description': 'The description for the gist',
'public': True or False,
'files': {
'the_filename.txt': {'content': 'Whatever the Gist file contents will be.'}
}
}
gist_id (str): The unique identifier of the gist to update. The gist_id can be found at the
end of your previosuly created Gist url. Not required when creating a new Gist. Defaults to ''.
action (str, optional): Specify to update and existing Gist or create a new one.
Values are either `'create'` or `'update'`. Defaults to 'update'.
Returns:
str: The json contents of the updated or new Gist page..
"""
if action not in ['create', 'update']:
raise ValueError('The action value must be either "create" or "update."')
access_url: str = 'https://api.github.com/gists'
# If you want to create a new gist file, do not append the gist_id to the access_url.
if action == 'update':
access_url = f'{access_url}/{gist_id}'
# Create json string to be uploaded and encode it.
json_data: bytes = json.dumps(payload).encode('utf-8')
headers: dict = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
}
# Post the data.
req: object = urllib.request.Request(access_url, json_data, headers)
with urllib.request.urlopen(req) as response:
gist_page = response.read() # The updated Gist page.
return gist_page