Skip to content

Commit

Permalink
using entry_points for setup now, use argparse for building commandli…
Browse files Browse the repository at this point in the history
…ne interface, fix 200 handling in get_docs
  • Loading branch information
dcolish authored and ericholscher committed Jun 16, 2011
1 parent ffd63f9 commit 58617fb
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 53 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1 +1,2 @@
docs/_build
*.egg-info
1 change: 0 additions & 1 deletion bin/rtfd

This file was deleted.

2 changes: 1 addition & 1 deletion docs/index.rst
Expand Up @@ -25,7 +25,7 @@ We have made it easy to get to the documentation for a project from the command

``rtd <project>`` - Open http://project.rtfd.org, which will redirect to its latest version.

``rtd <project> <slug>`` - Will open to http://project.rtfd.org/slug, which will kick off a query against RTD's backend store of slug to documentation mapping. If nothing exists for your slug, you will be asked to figure out where it should go for future users.
``rtd <project> <extra>`` - Will open to http://project.rtfd.org/slug, which will kick off a query against RTD's backend store of slug to documentation mapping. If nothing exists for your slug, you will be asked to figure out where it should go for future users.

Building docs
-------------
Expand Down
11 changes: 9 additions & 2 deletions setup.py
@@ -1,6 +1,6 @@
import codecs
try:
from setuptools import setup
from setuptools import setup, find_packages
extra_setup = dict(
zip_safe = True,
install_requires = ['httplib2'],
Expand All @@ -15,9 +15,16 @@
author='Eric Holscher',
author_email='eric@ericholscher.com',
url='http://github.com/ericholscher/rtd',
scripts=['bin/rtd'],
license='BSD',
description='Find documentation on Read the Docs!',
package_dir={'': 'src'},
packages=find_packages('src'),
long_description=codecs.open("README.rst", "r", "utf-8").read(),
entry_points={
'console_scripts': [
'rtd=rtd:main',
'rtfd=rtd:main'
]
},
**extra_setup
)
111 changes: 62 additions & 49 deletions bin/rtd → src/rtd.py 100755 → 100644
@@ -1,24 +1,27 @@
#!/usr/bin/env python
from argparse import ArgumentParser
import commands
import webbrowser
import httplib2
import sys
import json
import re
import os
import inspect

#I recommend reading this file from the bottom up.

BASE_SERVER = 'http://readthedocs.org'
API_SERVER = '%s/api/v1' % BASE_SERVER

VERBOSE = False
##################
# Helper Functions
##################


class NoRoutes(Exception):
pass


def _guess_repo(vcs):
if vcs == "git":
#This is mainly to handle writable github repos.
Expand All @@ -31,9 +34,11 @@ def _guess_repo(vcs):
else:
return None


def _get_auth_string(user, password):
return "Basic %s" % ("%s:%s" % (user, password)).encode("base64").strip()


def _read_creds(filename):
try:
line = open(filename).readline()
Expand All @@ -42,25 +47,28 @@ def _read_creds(filename):
un, pw = line.split(":")
return (un.strip(), pw.strip())


def _get_project_data(slug):
GET_URL = "%s/project/%s" % (API_SERVER, slug)
h = httplib2.Http(timeout=5)
resp, proj_info = h.request(GET_URL, "GET")
return json.loads(proj_info)


def _dump_results(resp, content):
print "Status: %s" % resp.status

########
# Routes
########

def create_project(vcs, name, repo=None):

def create_project(vcs, name, repo=''):
user, password = _read_creds(os.path.expanduser("~/.rtdrc"))
if not user:
user = raw_input("Username: ")
password = raw_input("Password: ")
if not repo:
if repo == '':
repo = _guess_repo(vcs)
if not repo:
print "Couldn't guess repo, please input it."
Expand All @@ -74,7 +82,7 @@ def create_project(vcs, name, repo=None):
})
h = httplib2.Http(timeout=5)
resp, content = h.request(post_url, "POST", body=post_data,
headers={'content-type':'application/json',
headers={'content-type': 'application/json',
'AUTHORIZATION': auth}
)
_dump_results(resp, content)
Expand All @@ -84,66 +92,71 @@ def create_project(vcs, name, repo=None):
print "URL for your project: %s"
webbrowser.open(proj_url)


def build_project(slug):
proj_obj = _get_project_data(slug)
post_url = "http://readthedocs.org/build/%s" % proj_obj['id']
h = httplib2.Http(timeout=5)
resp, content = h.request(post_url, "POST")
_dump_results(resp, content)

def get_docs(slug, extra=''):
URL = "%s/project/%s/" % (API_SERVER, slug)

def get_docs(project, extra=''):
URL = "%s/project/%s/" % (API_SERVER, project)
h = httplib2.Http(timeout=5)
try:
resp, content = h.request(URL, "GET")
except AttributeError:
#XXX:dc: Is this really what httplib2 raises?
print "Socket error trying to pull from Read the Docs"
return False
if resp['status'] == '200':
content_dict = json.loads(content)
print content_dict['description']
url = 'http://%s.rtfd.org/%s' % (slug, extra)
print "Opening browser to %s" % url
if extra:
url = 'http://%s.rtfd.org/%s' % (project, extra)
else:
url = 'http://%s.rtfd.org/' % project
if VERBOSE:
print "Opening browser to %s" % url
webbrowser.open(url)
print "Invalid return data"
_dump_results(resp, content)
return False

#This routing idea credited to Cody Soyland on butlertron :)
COMMAND_ROUTES = (
("create (?P<vcs>(git|hg)) (?P<name>[\w-]+) (?P<repo>.*)", create_project),
("create (?P<vcs>(git|hg)) (?P<name>[\w-]+)", create_project),
("build (?P<slug>[\w-]+)", build_project),
("(?P<slug>[\w-]+) (?P<extra>\w+)$", get_docs),
("(?P<slug>[\w-]+)$", get_docs),
("^$", lambda: webbrowser.open("http://readthedocs.org")),
)

class Dispatcher(object):
def __init__(self, routes):
self.routes = routes
def resolve(self, message):
for regex, responder in self.routes:
pattern = re.compile(regex, re.IGNORECASE)
match = pattern.match(message)
if match:
if isinstance(responder, Dispatcher):
try:
return responder.resolve(message)
except NoRoutes:
pass
else:
return responder, match.groupdict()
raise NoRoutes()

def dispatch(input):
dispatcher = Dispatcher(COMMAND_ROUTES)
try:
responder, arguments = dispatcher.resolve(input)
except NoRoutes:
print 'No route exists for "%s"' % input
return True
else:
responder(**arguments)
print "Invalid return data"
_dump_results(resp, content)
return False


if __name__ == "__main__":
dispatch(' '.join(sys.argv[1:]))
def main():
parser = ArgumentParser(prog="rtd")
parser.add_argument('--verbose', action='store_true')
subparsers = parser.add_subparsers()

get_docs_parser = subparsers.add_parser(
"get", help='get to the documentation for a project')
get_docs_parser.add_argument('project', metavar="PROJ", nargs='?')
get_docs_parser.add_argument('extra', metavar="EXTRA", nargs='?')
get_docs_parser.set_defaults(func=get_docs)

create_proj_parser = subparsers.add_parser(
"create", help='create a project')
create_proj_parser.add_argument('vcs', metavar="VCS", nargs=1,
help="hg or git")
create_proj_parser.add_argument('name', metavar="NAME", nargs=1)
create_proj_parser.add_argument('repo', metavar="REPO", nargs='?')
create_proj_parser.set_defaults(func=create_project)

build_proj_parser = subparsers.add_parser(
"build", help='build project docs')
build_proj_parser.add_argument('slug', metavar="SLUG", nargs=1,
help="url slug for the project")
build_proj_parser.set_defaults(func=build_project)

args = parser.parse_args()
if args.verbose:
global VERBOSE
VERBOSE = True
arg_dict = {}
for arg in inspect.getargspec(args.func).args:
arg_dict.update({arg: getattr(args, arg, '')})
args.func(**arg_dict)

0 comments on commit 58617fb

Please sign in to comment.