Browse Source

initial commit

master
Brett Langdon 11 years ago
commit
4d6cbc0864
14 changed files with 260 additions and 0 deletions
  1. +2
    -0
      .gitignore
  2. +22
    -0
      LICENSE
  3. +4
    -0
      MANIFEST.in
  4. +0
    -0
      README.md
  5. +25
    -0
      bin/greenrpc-client
  6. +24
    -0
      bin/greenrpc-server
  7. +4
    -0
      greenrpc/__init__.py
  8. +83
    -0
      greenrpc/base.py
  9. +29
    -0
      greenrpc/client.py
  10. +2
    -0
      greenrpc/error.py
  11. +31
    -0
      greenrpc/server.py
  12. +2
    -0
      requirements.txt
  13. +2
    -0
      setup.cfg
  14. +30
    -0
      setup.py

+ 2
- 0
.gitignore View File

@ -0,0 +1,2 @@
*.py[co]
*.egg-info

+ 22
- 0
LICENSE View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2014 Brett Langdon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 4
- 0
MANIFEST.in View File

@ -0,0 +1,4 @@
include README.* LICENSE setup.py setup.cfg
recursive-include greenrpc *.py
global-exclude *.pyc
global-exclude *.pyo

+ 0
- 0
README.md View File


+ 25
- 0
bin/greenrpc-client View File

@ -0,0 +1,25 @@
#!/usr/bin/env python
import argparse
from greenrpc import TCP_SERVER_DEFAULT_PORT
from greenrpc.client import TCPClient
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Start a new GreenRPC TCP Server")
parser.add_argument("method", metavar="<method>", type=str,
help="The remote method to call")
parser.add_argument("args", metavar="<arg>", nargs="*", type=str,
help="Arguments to send for the remote method call")
default_connect = "127.0.0.1:%s" % (TCP_SERVER_DEFAULT_PORT, )
parser.add_argument("--connect", dest="connect", type=str, default=default_connect,
help="<address>:<port> of the server to connect to(default: %s)" % (default_connect, ))
parser.add_argument("--debug", dest="debug", action="store_true", default=False,
help="Whether or not to show the full result")
args = parser.parse_args()
address, _, port = args.connect.partition(":")
client = TCPClient(connect=(address, int(port)))
result = client.call(args.method, args.args, debug=args.debug)
print result

+ 24
- 0
bin/greenrpc-server View File

@ -0,0 +1,24 @@
#!/usr/bin/env python
import argparse
from greenrpc import TCP_SERVER_DEFAULT_PORT
from greenrpc.server import TCPServer
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Start a new GreenRPC TCP Server")
parser.add_argument("module", metavar="<module>", type=str,
help="Python module to expose for the RPC Server")
default_bind = "127.0.0.1:%s" % (TCP_SERVER_DEFAULT_PORT, )
parser.add_argument("--bind", dest="bind", type=str, default=default_bind,
help="<address>:<port> to bind the server to (default: %s)" % (default_bind, ))
parser.add_argument("--spawn", dest="spawn", type=int, default=4,
help="number of greenlets to spawn (default: 4)")
args = parser.parse_args()
address, _, port = args.bind.partition(":")
server = TCPServer(args.module, bind=(address, int(port)), spawn=args.spawn)
try:
server.serve_forever()
except KeyboardInterrupt:
print "Stopping GreenRPC Server"
server.stop()

+ 4
- 0
greenrpc/__init__.py View File

@ -0,0 +1,4 @@
__version__ = "0.1.0"
TCP_SERVER_DEFAULT_PORT = 3434
HTTP_SERVER_DEFAULT_PORT = 3435

+ 83
- 0
greenrpc/base.py View File

@ -0,0 +1,83 @@
import time
import types
import msgpack
class BaseServer(object):
SOCKET_BUFFER_SIZE = 1024
def __init__(self, services):
if isinstance(services, (dict, types.ModuleType)):
self.services = services
elif isinstance(services, basestring):
self.services = __import__(services)
else:
raise TypeError("First argument to BaseServer.__init__ must be a dict or a string")
self.packer = msgpack.Packer()
def unpack_requests(self, sock):
unpacker = msgpack.Unpacker()
while True:
data = sock.recv(self.SOCKET_BUFFER_SIZE)
if not data:
break
unpacker.feed(data)
for request in unpacker:
yield request
def pack_result(self, result):
return self.packer.pack(result)
def handle_request(self, request):
start_time = time.time()
req_method = request.get("method")
req_args = request.get("args", [])
result = {
"id": request.get("id"),
"results": None,
}
if not req_method:
result["error"] = "No request method was provided"
elif not hasattr(self.services, req_method):
result["error"] = "Unknown request method '%s'" % (req_method, )
else:
try:
result["results"] = getattr(self.services, req_method)(*req_args)
except Exception, e:
result["error"] = e.message
result["run_time"] = (time.time() - start_time) * 1000.0
return result
class BaseClient(object):
SOCKET_BUFFER_SIZE = 1024
def __init__(self):
self.unpacker = msgpack.Unpacker()
self.packer = msgpack.Packer()
self.id = 0
def pack_request(self, id, method, args=[]):
return self.packer.pack({
"id": id,
"method": method,
"args": args,
})
def unpack_results(self, sock):
while True:
data = sock.recv(self.SOCKET_BUFFER_SIZE)
if not data:
break
self.unpacker.feed(data)
return self.unpacker.next()
def __getattr__(self, method):
def wrapper(*args):
return self.call(method, args)
return wrapper

+ 29
- 0
greenrpc/client.py View File

@ -0,0 +1,29 @@
from gevent.socket import socket
from greenrpc import TCP_SERVER_DEFAULT_PORT
from greenrpc.base import BaseClient
from greenrpc.error import RPCException
class TCPClient(BaseClient):
def __init__(self, connect=("127.0.0.1", TCP_SERVER_DEFAULT_PORT)):
super(TCPClient, self).__init__()
self.connection = socket()
self.connection.connect(connect)
self.fp = self.connection.makefile()
def call(self, method, args=[], debug=False):
self.id += 1
request = self.pack_request(self.id, method, args)
self.fp.write(request)
self.fp.flush()
results = self.unpack_results(self.connection)
if debug:
return results
if results.get("error"):
raise RPCException(results["error"])
return results.get("results")
class HTTPClient(BaseClient):
pass

+ 2
- 0
greenrpc/error.py View File

@ -0,0 +1,2 @@
class RPCException(Exception):
pass

+ 31
- 0
greenrpc/server.py View File

@ -0,0 +1,31 @@
import contextlib
from gevent.pywsgi import WSGIServer
from gevent.server import StreamServer
from greenrpc import TCP_SERVER_DEFAULT_PORT, HTTP_SERVER_DEFAULT_PORT
from greenrpc.base import BaseServer
class TCPServer(StreamServer, BaseServer):
def __init__(self, services, bind=("127.0.0.1", TCP_SERVER_DEFAULT_PORT), spawn=1):
StreamServer.__init__(self, bind, spawn=spawn)
BaseServer.__init__(self, services)
def handle(self, socket, address):
with contextlib.closing(socket) as sock:
with contextlib.closing(sock.makefile()) as fp:
for request in self.unpack_requests(sock):
result = self.handle_request(request)
fp.write(self.pack_result(result))
fp.flush()
class WSGIServer(WSGIServer, BaseServer):
def __init__(self, services, bind=("127.0.0.1", HTTP_SERVER_DEFAULT_PORT)):
WSGIServer.__init__(self, bind)
BaseServer.__init__(self, services)
def application(self, environ, start_response):
start_response("200 OK", [("Content-Type", "text/plain")])
return ["Hello, Worlds"]

+ 2
- 0
requirements.txt View File

@ -0,0 +1,2 @@
gevent==1.0.1
msgpack-python==0.4.2

+ 2
- 0
setup.cfg View File

@ -0,0 +1,2 @@
[metadata]
description-file = README.md

+ 30
- 0
setup.py View File

@ -0,0 +1,30 @@
#!/usr/bin/env python
from setuptools import setup, find_packages
from greenrpc import __version__
with open("./requirements.txt") as fp:
requirements = fp.read()
requirements = requirements.split("\n")
setup(
name="greenrpc",
version=__version__,
description="msgpack TCP & HTTP RPC Servers written with gevent",
author="Brett Langdon",
author_email="brett@blangdon.com",
url="https://github.com/brettlangdon/greenrpc",
packages=find_packages(),
license="MIT",
scripts=["bin/greenrpc-server", "bin/greenrpc-client"],
install_requires=requirements,
classifiers=[
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"License :: OSI Approved :: MIT License",
"Topic :: Utilities",
]
)

Loading…
Cancel
Save