• Stars
    star
    6,132
  • Rank 6,350 (Top 0.2 %)
  • Language
    Python
  • License
    MIT License
  • Created over 11 years ago
  • Updated 12 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Preview GitHub README.md files locally before committing them.

Grip -- GitHub Readme Instant Preview

Current version on PyPI Say Thanks!

Render local readme files before sending off to GitHub.

Grip is a command-line server application written in Python that uses the GitHub markdown API to render a local readme file. The styles and rendering come directly from GitHub, so you'll know exactly how it will appear. Changes you make to the Readme will be instantly reflected in the browser without requiring a page refresh.

Motivation

Sometimes you just want to see the exact readme result before committing and pushing to GitHub.

Especially when doing Readme-driven development.

Installation

To install grip, simply:

$ pip install grip

On OS X, you can also install with Homebrew:

$ brew install grip

Usage

To render the readme of a repository:

$ cd myrepo
$ grip
 * Running on http://localhost:6419/

Now open a browser and visit http://localhost:6419. Or run with -b and Grip will open a new browser tab for you.

You can also specify a port:

$ grip 80
 * Running on http://localhost:80/

Or an explicit file:

$ grip AUTHORS.md
 * Running on http://localhost:6419/

Alternatively, you could just run grip and visit localhost:6419/AUTHORS.md since grip supports relative URLs.

You can combine the previous examples. Or specify a hostname instead of a port. Or provide both.

$ grip AUTHORS.md 80
 * Running on http://localhost:80/
$ grip CHANGES.md 0.0.0.0
 * Running on http://0.0.0.0:6419/
$ grip . 0.0.0.0:80
 * Running on http://0.0.0.0:80/

You can even bypass the server and export to a single HTML file, with all the styles and assets inlined:

$ grip --export
Exporting to README.html

Control the output name with the second argument:

$ grip README.md --export index.html
Exporting to index.html

If you're exporting a bunch of files, you can prevent styles from being inlining to save space with --no-inline:

$ grip README.md --export --no-inline introduction.html
Exporting to introduction.html

Reading and writing from stdin and stdout is also supported, allowing you to use Grip with other programs:

$ cat README.md | grip -
 * Running on http://localhost:6419/
$ grip AUTHORS.md --export - | bcat
$ cat README.md | grip --export - | less

This allows you to quickly test how things look by entering Markdown directly in your terminal:

$ grip -
Hello **world**!
^D
 * Running on http://localhost:6419/

Note: ^D means Ctrl+D, which works on Linux and OS X. On Windows you'll have to use Ctrl+Z.

Rendering as user-content like comments and issues is also supported, with an optional repository context for linking to issues:

$ grip --user-content --context=joeyespo/grip
 * Running on http://localhost:6419/

For more details and additional options, see the help:

$ grip -h

Access

Grip strives to be as close to GitHub as possible. To accomplish this, grip uses GitHub's Markdown API so that changes to their rendering engine are reflected immediately without requiring you to upgrade grip. However, because of this you may hit the API's hourly rate limit. If this happens, grip offers a way to access the API using your credentials to unlock a much higher rate limit.

$ grip --user <your-username> --pass <your-password>

Or use a personal access token with an empty scope (note that a token is required if your GitHub account is set up with two-factor authentication):

$ grip --pass <token>

You can persist these options in your local configuration. For security purposes, it's highly recommended that you use an access token over a password. (You could also keep your password safe by configuring Grip to grab your password from a password manager.)

There's also a work-in-progress branch to provide offline rendering. Once this resembles GitHub more precisely, it'll be exposed in the CLI, and will ultimately be used as a seamless fallback engine for when the API can't be accessed.

Grip always accesses GitHub over HTTPS, so your README and credentials are protected.

Tips

Here's how others from the community are using Grip.

Want to share your own? Say hello @joeyespo or submit a pull request.

Create a local mirror of a Github Wiki

$ git clone https://github.com/YOUR_USERNAME/YOUR_REPOSITORY.wiki.git
$ cd YOUR_REPOSITORY.wiki
$ grip

By Joshua Gourneau.

Generate HTML documentation from a collection of linked README files

  1. Enter the directory:

    $ cd YOUR_DIR
    $ export GRIPURL=$(pwd)
  2. Include all assets by setting the CACHE_DIRECTORY config variable:

    $ echo "CACHE_DIRECTORY = '$(pwd)/assets'" >> ~/.grip/settings.py
  3. Export all your Markdown files with Grip and replace absolute asset paths with relative paths:

    $ for f in *.md; do grip --export $f --no-inline; done
    $ for f in *.html; do sed -i '' "s?$GRIPURL/??g" $f; done

You can optionally compress the set of HTML files to docs.tgz with:

$ tar -czvf docs.tgz `ls | grep [\.]html$` assets

Looking for a cross platform solution? Here's an equivalent Python script.

By Matthew R. Tanudjaja.

Configuration

To customize Grip, create ~/.grip/settings.py, then add one or more of the following variables:

  • HOST: The host to use when not provided as a CLI argument, localhost by default
  • PORT: The port to use when not provided as a CLI argument, 6419 by default
  • DEBUG: Whether to use Flask's debugger when an error happens, False by default
  • DEBUG_GRIP: Prints extended information when an error happens, False by default
  • API_URL: Base URL for the github API, for example that of a Github Enterprise instance. https://api.github.com by default
  • CACHE_DIRECTORY: The directory, relative to ~/.grip, to place cached assets (this gets run through the following filter: CACHE_DIRECTORY.format(version=__version__)), 'cache-{version}' by default
  • AUTOREFRESH: Whether to automatically refresh the Readme content when the file changes, True by default
  • QUIET: Do not print extended information, False by default
  • STYLE_URLS: Additional URLs that will be added to the rendered page, [] by default
  • USERNAME: The username to use when not provided as a CLI argument, None by default
  • PASSWORD: The password or personal access token to use when not provided as a CLI argument (Please don't save your passwords here. Instead, use an access token or drop in this code grab your password from a password manager), None by default

Note that this is a Python file. If you see 'X' is not defined errors, you may have overlooked some quotes. For example:

USERNAME = 'your-username'
PASSWORD = 'your-personal-access-token'

Environment variables

  • GRIPHOME: Specify an alternative settings.py location, ~/.grip by default
  • GRIPURL: The URL of the Grip server, /__/grip by default

Advanced

This file is a normal Python script, so you can add more advanced configuration.

For example, to read a setting from the environment and provide a default value when it's not set:

PORT = os.environ.get('GRIP_PORT', 8080)

API

You can access the API directly with Python, using it in your own projects:

from grip import serve

serve(port=8080)
 * Running on http://localhost:8080/

Run main directly:

from grip import main

main(argv=['-b', '8080'])
 * Running on http://localhost:8080/

Or access the underlying Flask application for even more flexibility:

from grip import create_app

grip_app = create_app(user_content=True)
# Use in your own app

Documentation

serve

Runs a local server and renders the Readme file located at path when visited in the browser.

serve(path=None, host=None, port=None, user_content=False, context=None, username=None, password=None, render_offline=False, render_wide=False, render_inline=False, api_url=None, title=None, autorefresh=True, browser=False, grip_class=None)
  • path: The filename to render, or the directory containing your Readme file, defaulting to the current working directory
  • host: The host to listen on, defaulting to the HOST configuration variable
  • port: The port to listen on, defaulting to the PORT configuration variable
  • user_content: Whether to render a document as user-content like user comments or issues
  • context: The project context to use when user_content is true, which takes the form of username/project
  • username: The user to authenticate with GitHub to extend the API limit
  • password: The password to authenticate with GitHub to extend the API limit
  • render_offline: Whether to render locally using Python-Markdown (Note: this is a work in progress)
  • render_wide: Whether to render a wide page, False by default (this has no effect when used with user_content)
  • render_inline: Whether to inline the styles within the HTML file
  • api_url: A different base URL for the github API, for example that of a Github Enterprise instance. The default is the public API https://api.github.com.
  • title: The page title, derived from path by default
  • autorefresh: Automatically update the rendered content when the Readme file changes, True by default
  • browser: Open a tab in the browser after the server starts., False by default
  • grip_class: Use a custom Grip class

export

Writes the specified Readme file to an HTML file with styles and assets inlined.

export(path=None, user_content=False, context=None, username=None, password=None, render_offline=False, render_wide=False, render_inline=True, out_filename=None, api_url=None, title=None, quiet=None, grip_class=None)
  • path: The filename to render, or the directory containing your Readme file, defaulting to the current working directory
  • user_content: Whether to render a document as user-content like user comments or issues
  • context: The project context to use when user_content is true, which takes the form of username/project
  • username: The user to authenticate with GitHub to extend the API limit
  • password: The password to authenticate with GitHub to extend the API limit
  • render_offline: Whether to render locally using Python-Markdown (Note: this is a work in progress)
  • render_wide: Whether to render a wide page, False by default (this has no effect when used with user_content)
  • render_inline: Whether to inline the styles within the HTML file (Note: unlike the other API functions, this defaults to True)
  • out_filename: The filename to write to, <in_filename>.html by default
  • api_url: A different base URL for the github API, for example that of a Github Enterprise instance. The default is the public API https://api.github.com.
  • title: The page title, derived from path by default
  • quiet: Do not print to the terminal
  • grip_class: Use a custom Grip class

create_app

Creates a Flask application you can use to render and serve the Readme files. This is the same app used by serve and export and initializes the cache, using the cached styles when available.

create_app(path=None, user_content=False, context=None, username=None, password=None, render_offline=False, render_wide=False, render_inline=False, api_url=None, title=None, text=None, grip_class=None)
  • path: The filename to render, or the directory containing your Readme file, defaulting to the current working directory
  • user_content: Whether to render a document as user-content like user comments or issues
  • context: The project context to use when user_content is true, which takes the form of username/project
  • username: The user to authenticate with GitHub to extend the API limit
  • password: The password to authenticate with GitHub to extend the API limit
  • render_offline: Whether to render locally using Python-Markdown (Note: this is a work in progress)
  • render_wide: Whether to render a wide page, False by default (this has no effect when used with user_content)
  • render_inline: Whether to inline the styles within the HTML file
  • api_url: A different base URL for the github API, for example that of a Github Enterprise instance. The default is the public API https://api.github.com.
  • title: The page title, derived from path by default
  • text: A string or stream of Markdown text to render instead of being loaded from path (Note: path can be used to set the page title)
  • grip_class: Use a custom Grip class

render_app

Renders the application created by create_app and returns the HTML that would normally appear when visiting that route.

render_app(app, route='/')
  • app: The Flask application to render
  • route: The route to render, '/' by default

render_content

Renders the specified markdown text without caching.

render_content(text, user_content=False, context=None, username=None, password=None, render_offline=False, api_url=None, title=None)
  • text: The Markdown text to render
  • user_content: Whether to render a document as user-content like user comments or issues
  • context: The project context to use when user_content is true, which takes the form of username/project
  • username: The user to authenticate with GitHub to extend the API limit
  • password: The password to authenticate with GitHub to extend the API limit
  • render_offline: Whether to render locally using Python-Markdown (Note: this is a work in progress)
  • api_url: A different base URL for the github API, for example that of a Github Enterprise instance. This is required when not using the offline renderer.
  • title: The page title, derived from path by default

render_page

Renders the markdown from the specified path or text, without caching, and returns an HTML page that resembles the GitHub Readme view.

render_page(path=None, user_content=False, context=None, username=None, password=None, render_offline=False, render_wide=False, render_inline=False, api_url=None, title=None, text=None, quiet=None, grip_class=None)
  • path: The path to use for the page title, rendering 'README.md' if None
  • user_content: Whether to render a document as user-content like user comments or issues
  • context: The project context to use when user_content is true, which takes the form of username/project
  • username: The user to authenticate with GitHub to extend the API limit
  • password: The password to authenticate with GitHub to extend the API limit
  • render_offline: Whether to render offline using Python-Markdown (Note: this is a work in progress)
  • render_wide: Whether to render a wide page, False by default (this has no effect when used with user_content)
  • render_inline: Whether to inline the styles within the HTML file
  • api_url: A different base URL for the github API, for example that of a Github Enterprise instance. The default is the public API https://api.github.com.
  • title: The page title, derived from path by default
  • text: A string or stream of Markdown text to render instead of being loaded from path (Note: path can be used to set the page title)
  • quiet: Do not print to the terminal
  • grip_class: Use a custom Grip class

clear_cache

Clears the cached styles and assets.

clear_cache(grip_class=None)

main

Runs Grip with the specified arguments.

main(argv=None, force_utf8=True)
  • argv: The arguments to run with, sys.argv[1:] by default
  • force_utf8: Sets the default encoding to utf-8 in the current Python instance. This has no effect on Python 3 since Unicode is handled by default

Classes

class Grip(Flask)

A Flask application that can serve a file or directory containing a README.

Grip(source=None, auth=None, renderer=None, assets=None, render_wide=None, render_inline=None, title=None, autorefresh=None, quiet=None, grip_url=None, static_url_path=None, instance_path=None, **kwargs)
default_renderer

Returns the default renderer using the current config. This is only used if renderer is set to None in the constructor.

Grip.default_renderer()
default_asset_manager

Returns the default asset manager using the current config. This is only used if asset_manager is set to None in the constructor.

Grip.default_asset_manager()
add_content_types

Adds the application/x-font-woff and application/octet-stream content types if they are missing. Override to add additional content types on initialization.

Grip.add_content_types()
clear_cache

Clears the downloaded assets.

Grip.clear_cache()
render

Renders the application and returns the HTML unicode that would normally appear when visiting in the browser.

Grip.render(route=None)
  • route: The route to render, / by default
run

Starts a server to render the README. This calls Flask.run internally.

Grip.run(host=None, port=None, debug=None, use_reloader=None, open_browser=False)
  • host: The hostname to listen on. Set this to '0.0.0.0' to have the server available externally as well, 'localhost' by default
  • port: The port of the webserver. Defaults to 6419
  • debug: If given, enable or disable debug mode. See Flask.debug.
  • use_reloader: Should the server automatically restart the python process if modules were changed? False by default unless the DEBUG_GRIP setting is specified.
  • open_browser: Opens the browser to the address when the server starts

class AlreadyRunningError(RuntimeError)

Raised when Grip.run is called while the server is already running.

AlreadyRunningError()

class ReadmeNotFoundError(NotFoundError or IOError)

Raised when the specified Readme could not be found.

ReadmeNotFoundError(path=None, message=None)

class ReadmeAssetManager(object)

Manages the style and font assets rendered with Readme pages. This is an abstract base class.

ReadmeAssetManager(cache_path, style_urls=None)

class GitHubAssetManager(ReadmeAssetManager)

Manages the style and font assets rendered with Readme pages. Set cache_path to None to disable caching.

class ReadmeReader(object)

Reads Readme content from a URL subpath. This is an abstract base class.

ReadmeReader()

class DirectoryReader(ReadmeReader)

Reads Readme files from URL subpaths.

DirectoryReader(path=None, silent=False)

class TextReader(ReadmeReader)

Reads Readme content from the provided unicode string.

TextReader(text, display_filename=None)

class StdinReader(TextReader)

Reads Readme text from STDIN.

StdinReader(display_filename=None)

class ReadmeRenderer(object)

Renders the Readme. This is an abstract base class.

ReadmeRenderer(user_content=None, context=None)

class GitHubRenderer(ReadmeRenderer)

Renders the specified Readme using the GitHub Markdown API.

GitHubRenderer(user_content=None, context=None, api_url=None, raw=None)

class OfflineRenderer(ReadmeRenderer)

Renders the specified Readme locally using pure Python. Note: This is currently an incomplete feature.

OfflineRenderer(user_content=None, context=None)

Constants

SUPPORTED_TITLES

The common Markdown file titles on GitHub.

SUPPORTED_TITLES = ['README', 'Home']
  • filename: The UTF-8 file to read.

SUPPORTED_EXTENSIONS

The supported extensions, as defined by GitHub.

SUPPORTED_EXTENSIONS = ['.md', '.markdown']

DEFAULT_FILENAMES

This constant contains the names Grip looks for when no file is provided.

DEFAULT_FILENAMES = [title + ext
                     for title in SUPPORTED_TITLES
                     for ext in SUPPORTED_EXTENSIONS]

DEFAULT_FILENAME

This constant contains the default Readme filename, namely:

DEFAULT_FILENAME = DEFAULT_FILENAMES[0]  # README.md

DEFAULT_GRIPHOME

This constant points to the default value if the GRIPHOME environment variable is not specified.

DEFAULT_GRIPHOME = '~/.grip'

DEFAULT_GRIPURL

The default URL of the Grip server and all its assets:

DEFAULT_GRIPURL = '/__/grip'

DEFAULT_API_URL

The default app_url value:

DEFAULT_API_URL = 'https://api.github.com'

Testing

Install the package and test requirements:

$ pip install -e .[tests]

Run tests with pytest:

$ pytest

Or to re-run tests as you make changes, use pytest-watch:

$ ptw

External assumption tests

If you're experiencing a problem with Grip, it's likely that an assumption made about the GitHub API has been broken. To verify this, run:

$ pytest -m assumption

Since the external assumptions rely on an internet connection, you may want to skip them when developing locally. Tighten the cycle even further by stopping on the first failure with -x:

$ pytest -xm "not assumption"

Or with pytest-watch:

$ ptw -- -xm "not assumption"

Contributing

  1. Check the open issues or open a new issue to start a discussion around your feature idea or the bug you found
  2. Fork the repository and make your changes
  3. Open a new pull request

If your PR has been waiting a while, feel free to ping me on Twitter.

Use this software often? Say Thanks! 😃

More Repositories

1

pytest-watch

Local continuous test runner with pytest and watchdog.
Python
703
star
2

gitpress

Blissful blogging for hackers.
Python
79
star
3

inbox-by-gmail-checker

Displays the number of unread messages in Inbox by Gmail. You can also click the button to open your inbox.
JavaScript
69
star
4

py-getch

Portable getch() for Python.
Python
56
star
5

sublimetext-console-exec

Run a Sublime Text command in a console window. Supports both Sublime Text 2 & 3.
Python
36
star
6

django-q-email

Queues the sending of email with Django Q.
Python
33
star
7

flask-pytest

Runs pytest in a background process when DEBUG is True.
Python
28
star
8

hello-redis-tasks

An example of how to use Redis as a task queue within a Flask web application.
Python
26
star
9

window-finder-control

A .NET control that behaves like the Spy++ window finder tool, allowing the user to select a top-level window by dragging a crosshair icon over it.
C#
26
star
10

checkers

The game of Checkers, supporting single player with different difficulties, two player, and an online game with full hosting and chat support.
C#
23
star
11

upgrade-requirements.py

Upgrade all your outdated requirements.txt in a single command.
Python
14
star
12

gistmail

Get email summaries of websites.
Python
13
star
13

flask-s3-example

A quick example on how to directly upload to S3 using Flask.
JavaScript
13
star
14

autohotkey-scripts

My currently running AutoHotKey scripts.
AutoHotkey
12
star
15

flask-scaffold

A scaffold for quickly developing a Flask project on a production server using Passenger.
Python
11
star
16

py-impactjs

Implements a back-end server in Python for Impactjs that lets you develop multiple games at once. There's also a basic UI for browsing your games, creating new ones, and adding assets to your project.
Python
10
star
17

console-flash

A simple command-line application for Windows to flash a command window, letting you know when a script finishes. A common place to use this at the end of a batch file.
C#
10
star
18

django-werkzeug

Enhances Django runserver with Werkzeug.
Python
9
star
19

github-version-tags

Displays the latest version of a project next to the repo name on GitHub.
JavaScript
8
star
20

webcam-lib

A .NET library that provides a simplified API to interact with a webcam device.
C#
7
star
21

gesso.js

A framework and CLI tool for rapidly creating HTML <canvas> projects.
JavaScript
7
star
22

tabhouse

A web application that lets you find quality guitar tabs.
Python
7
star
23

minesweeper

My first Flash game written in ActionScript 3.
ActionScript
6
star
24

glance.py

Highlight stack traces so you can glance at your terminal instead of studying it.
Python
4
star
25

tray-me

Add any Windows application to the tray area.
C#
4
star
26

hack-function

"Goto Definition" for the Python Interpreter
Python
4
star
27

nosey

Local continuous test runner with nose and watchdog.
Python
4
star
28

google-apps-client

Combines the ease and familiarity of Outlook's notifications with the power of Google Apps.
C#
4
star
29

study-guide

A fun, high-tech alternative to flashcards [2004]
C#
3
star
30

jsonpointer-cli

CLI for retrieving a specific value within a JSON document.
JavaScript
3
star
31

snap-it

Allows you to take snapshots from the webcam at set intervals for creating time-lapse videos.
C#
3
star
32

path-and-address

Functions for server CLI applications used by humans.
Python
3
star
33

facebook-photobook

A tool that allows you to download your photos from Facebook, including any photos you've been tagged in.
C#
3
star
34

mockdown

Add basic interactivity to your mockups for demos and developers.
Python
3
star
35

ghost-heroku-kit

Run Ghost on Heroku with as little effort as possible.
CSS
2
star
36

prepend

Prepends a list of files with text.
Python
2
star
37

gitpress-blog

Blissful blogging for hackers. The Blog.
2
star
38

todos-api

Example mobile TODO app.
Python
2
star
39

joeyespo.github.io

The content for joeyespo.com.
HTML
2
star
40

income-comparer

Quickly calculate and compare income.
HTML
2
star
41

guestbook-aggregator

An example website that aggregates guestbooks using a REST API.
Python
1
star
42

ituner

A utility that interfaces with iTunes to provide additional functionality.
C#
1
star
43

gmail-button-experiment

An experiment to help me be more productive in my day-to-day by checking email less often.
JavaScript
1
star
44

todos-app

Example mobile TODO app.
JavaScript
1
star
45

academica

JavaScript
1
star
46

sublimetext-settings

My Sublime Text 2 settings for syncing between platforms and devices.
1
star
47

drawing-visualizer

A Visual Studio visualizer to see images and bitmaps in variables while debugging code.
C#
1
star
48

import

Shorthand for typing 'python' then 'import some-module' in the terminal.
Batchfile
1
star
49

danespo

My brother's blog.
CSS
1
star
50

mvrnd

Move a file at random.
Python
1
star
51

rethinkhost.com

The website for RethinkHost.
JavaScript
1
star
52

flask-guestbook-api

This consumes both APIs demonstrated during the PghPy Flask API meetup.
Python
1
star
53

parkhillchiropracticcenter.com

The information website for Dr. Tad Parkhill.
HTML
1
star
54

iwidgets.aggrenda.com

Renders Aggrenda widgets as images.
JavaScript
1
star