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)?

Asked By: age97701

||

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)
Answered By: Bertrand Martel

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"
Answered By: jar

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
Answered By: Jason Callahan
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.