# HG changeset patch # User Jeff Hammel # Date 1355077303 28800 # Node ID cfcfa093e4b4c432f57521735331903694e6a3bc initial commit diff -r 000000000000 -r cfcfa093e4b4 INSTALL.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/INSTALL.py Sun Dec 09 10:21:43 2012 -0800 @@ -0,0 +1,67 @@ +#!/usr/bin/env python + +""" +installation script for WSGraph +WSGI graph server +""" + +import os +import sys +import urllib2 +import subprocess +try: + from subprocess import check_call as call +except: + from subprocess import call + +REPO='http://k0s.org/hg/WSGraph' +DEST='WSGraph' # name of the virtualenv +VIRTUALENV='https://raw.github.com/pypa/virtualenv/develop/virtualenv.py' + +def which(binary, path=os.environ['PATH']): + dirs = path.split(os.pathsep) + for dir in dirs: + if os.path.isfile(os.path.join(dir, fileName)): + return os.path.join(dir, fileName) + if os.path.isfile(os.path.join(dir, fileName + ".exe")): + return os.path.join(dir, fileName + ".exe") + +def main(args=sys.argv[1:]): + + # create a virtualenv + virtualenv = which('virtualenv') or which('virtualenv.py') + if virtualenv: + call([virtualenv, DEST]) + else: + process = subproces.Popen([sys.executable, '-', DEST], stdin=subprocess.PIPE) + process.communicate(stdin=urllib2.urlopen(VIRTUALENV).read()) + + # create a src directory + src = os.path.join(DEST, 'src') + os.mkdir(src) + + # clone the repository + call(['hg', 'clone', REPO], cwd=src) + + # find the virtualenv python + python = None + for path in (('bin', 'python'), ('Scripts', 'python.exe')): + _python = os.path.join(DEST, *path) + if os.path.exists(_python) + python = _python + break + else: + raise Exception("Python binary not found in %s" % DEST) + + # find the clone + filename = REPO.rstrip('/') + filename = filename.split('/')[-1] + clone = os.path.join(src, filename) + assert os.path.exists(clone), "Clone directory not found in %s" % src + + # ensure setup.py exists + assert os.path.exists(os.path.join(clone, 'setup.py')), 'setup.py not found in %s' % clone + + # install the package in develop mode + call([python 'setup.py', 'develop'], cwd=clone) + diff -r 000000000000 -r cfcfa093e4b4 README.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.txt Sun Dec 09 10:21:43 2012 -0800 @@ -0,0 +1,11 @@ +WSGraph +=========== + +WSGI graph server + +---- + +Jeff Hammel + +http://k0s.org/hg/WSGraph + diff -r 000000000000 -r cfcfa093e4b4 setup.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup.py Sun Dec 09 10:21:43 2012 -0800 @@ -0,0 +1,45 @@ +""" +setup packaging script for WSGraph +""" + +import os + +version = "0.0" +dependencies = ['MakeItSo', 'webob'] + +# allow use of setuptools/distribute or distutils +kw = {} +try: + from setuptools import setup + kw['entry_points'] = """ + [console_scripts] + WSGraph = WSGraph.main:main + WSGraph-template = WSGraph.template:main +""" + kw['install_requires'] = dependencies +except ImportError: + from distutils.core import setup + kw['requires'] = dependencies + +try: + here = os.path.dirname(os.path.abspath(__file__)) + description = file(os.path.join(here, 'README.txt')).read() +except IOError: + description = '' + + +setup(name='WSGraph', + version=version, + description="WSGI graph server", + long_description=description, + classifiers=[], # Get strings from http://www.python.org/pypi?%3Aaction=list_classifiers + author='Jeff Hammel', + author_email='jhammel@mozilla.com', + url='http://k0s.org/hg/WSGraph', + license='', + packages=['wsgraph'], + include_package_data=True, + zip_safe=False, + **kw + ) + diff -r 000000000000 -r cfcfa093e4b4 tests/doctest.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/doctest.txt Sun Dec 09 10:21:43 2012 -0800 @@ -0,0 +1,11 @@ +Test WSGraph +================ + +The obligatory imports: + + >>> import wsgraph + +Run some tests. This test will fail, please fix it: + + >>> assert True == False + diff -r 000000000000 -r cfcfa093e4b4 tests/test.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test.py Sun Dec 09 10:21:43 2012 -0800 @@ -0,0 +1,61 @@ +#!/usr/bin/env python + +""" +doctest runner +""" + +import doctest +import os +import sys +from optparse import OptionParser + + +def run_tests(raise_on_error=False, report_first=False): + + # add results here + results = {} + + # doctest arguments + directory = os.path.dirname(os.path.abspath(__file__)) + extraglobs = {'here': directory} + doctest_args = dict(extraglobs=extraglobs, raise_on_error=raise_on_error) + doctest_args['optionsflags'] = doctest.ELLIPSIS + if report_first: + doctest_args['optionflags'] |= doctest.REPORT_ONLY_FIRST_FAILURE + + # gather tests + tests = [test for test in os.listdir(directory) + if test.endswith('.txt')] + + # run the tests + for test in tests: + try: + results[test] = doctest.testfile(test, **doctest_args) + except doctest.DocTestFailure, failure: + raise + except doctest.UnexpectedException, failure: + raise failure.exc_info[0], failure.exc_info[1], failure.exc_info[2] + + return results + +def main(args=sys.argv[1:]): + + # parse command line args + parser = OptionParser(description=__doc__) + parser.add_option('--raise', dest='raise_on_error', + default=False, action='store_true', + help="raise on first error") + parser.add_option('--report-first', dest='report_first', + default=False, action='store_true', + help="report the first error only (all tests will still run)") + options, args = parser.parse_args(args) + + # run the tests + results = run_tests(**options.__dict__) + if sum([i.failed for i in results.values()]): + sys.exit(1) # error + + +if __name__ == '__main__': + main() + diff -r 000000000000 -r cfcfa093e4b4 tests/unit.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/unit.py Sun Dec 09 10:21:43 2012 -0800 @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +""" +unit tests +""" + +import os +import sys +import unittest + +# globals +here = os.path.dirname(os.path.abspath(__file__)) + +class wsgraphUnitTest(unittest.TestCase): + + def test_wsgraph(self): + pass + +if __name__ == '__main__': + unittest.main() + diff -r 000000000000 -r cfcfa093e4b4 wsgraph/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wsgraph/__init__.py Sun Dec 09 10:21:43 2012 -0800 @@ -0,0 +1,3 @@ +# +from main import * + diff -r 000000000000 -r cfcfa093e4b4 wsgraph/main.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wsgraph/main.py Sun Dec 09 10:21:43 2012 -0800 @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +""" +WSGI graph server +""" + +import sys +import optparse + +def main(args=sys.argv[:]): + + # parse command line options + usage = '%prog [options]' + class PlainDescriptionFormatter(optparse.IndentedHelpFormatter): + """description formatter for console script entry point""" + def format_description(self, description): + if description: + return description.strip() + '\n' + else: + return '' + parser = optparse.OptionParser(usage=usage, description=__doc__, formatter=PlainDescriptionFormatter()) + options, args = parser.parse_args(args) + +if __name__ == '__main__': + main() + diff -r 000000000000 -r cfcfa093e4b4 wsgraph/model.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wsgraph/model.py Sun Dec 09 10:21:43 2012 -0800 @@ -0,0 +1,54 @@ +from abc import abstractmethod + +class GraphModel(object): + + @abstractmethod + def node(self, name, **values): + """get or set a node""" + + @abstractmethod + def nodes(self): + """returns a list of all nodes""" + + @abstractmethod + def edges(self): + """returns a list of all edges""" + + @abstractmethod + def edge(self, node1, node2, **values): + """returns edge from node1 to node2""" + + def __getitem__(self, key): + """ + if key is a basestring, return the node of that name; + if key is a 2-tuple/list, return the edge of that name + """ + + +class MemoryCache(GraphModel): + + def __init__(self): + self._edges = {} + self._nodes = {} + + def node(self, name, **values): + if values: + # setter + self._nodes[name] = values + else: + # getter + return self._nodes.get(name, None) + + def nodes(self): + return self._nodes.keys() + + def edge(self, node1, node2, **values): + if values: + # setter + self._edges[(node1, node2)] = values + else: + # getter + return self._edges.get((node1, node2), None) + + def edges(self): + return self._edges.keys() diff -r 000000000000 -r cfcfa093e4b4 wsgraph/web.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wsgraph/web.py Sun Dec 09 10:21:43 2012 -0800 @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +""" +web handler for WSGraph + +Formatters are keyed off of: +- (TODO) + +formatters = {0: { +} +""" + +import json +from webob import Request, Response, exc + +def JSONFormatter(**kwargs): + return json.dumps(kwargs, sort_keys=True) + +def JSONGraphFormatter(graph): + return json.dumps({'nodes': graph.nodes(), + 'edges': graph.edges()}, + sort_keys=True) + +class Dispatcher(object): + + def __init__(self, graph, + graph_formatters=None, # TODO + node_formatters=None, # TODO + edge_formatters=None, # TODO + require_auth=False): + self.graph = graph + self.require_auth = require_auth + self.formatters = {0: JSONGraphFormatter, + 1: JSONFormatter, + 2: JSONFormatter} + + def __call__(self, environ, start_response): + request = Request(environ) + import pdb; pdb.set_trace() + return response(environ, start_response) + + @staticmethod + def path_segments(request): + import pdb; pdb.set_trace() + + # HTTP methods + + def GET(self, request): + """respond to a GET request""" + + segments = self.path_segments(request) + + # formatter + formatter = self.formatters[len(segments)] + return Response(content_type='text/plain', + body="WSGraph") + + def POST(self, request): + """respond to a POST request""" + raise NotImplementedError + + def HEAD(self, request): + """respond to a HEAD request""" + raise NotImplementedError + +if __name__ == '__main__': + + # imports + from wsgiref import simple_server + from .model import MemoryCache + + graph = MemoryCache() + + app = Dispatcher(graph=graph) + server = simple_server.make_server(host='0.0.0.0', port=8080, app=app) + server.serve_forever() +