Skip to content
This repository has been archived by the owner on May 13, 2020. It is now read-only.

Commit

Permalink
New Features:
Browse files Browse the repository at this point in the history
- New improved documentation

- Support for writing request handlers in an imperative style using
  generators.

- Cleaner testing interfaces

- Added a new blocking client request interface,
  ``zc.ngi.blocking.request``.  Other older blocking APIs are
  deprecated.

- Dropped support for Python 2.4.

Bugs Fixed:

- The ``Sized`` request adapter's ``writelines`` method was broken.

- There we a number of problems with error handling in the ``async``
  implementation.
  • Loading branch information
Jim Fulton committed Jun 20, 2010
2 parents d89e37a + b96b187 commit 6410299
Show file tree
Hide file tree
Showing 18 changed files with 2,139 additions and 326 deletions.
21 changes: 12 additions & 9 deletions README.txt
@@ -1,17 +1,20 @@
*************************
Network Gateway Interface
*************************

Network programs are typically difficult to test because they require
setting up network connections, clients, and servers. In addition,
application code gets mixed up with networking code.
The Network Gateway Interface provides:

- the ability to test application networking code without use of
sockets, threads or subprocesses

- clean separation of application code and low-level networking code

- a fairly simple inheritence free set of networking APIs

The Network Gateway Interface (NGI) seeks to improve this situation by
separating application code from network code. This allows
application and network code to be tested independently and provides
greater separation of concerns.
- an event-based framework that makes it easy to handle many
simultaneous connections while still supporting an imperative
programming style.

.. contents::
To learn more, see http://packages.python.org/zc.ngi/

Changes
*******
Expand Down
18 changes: 8 additions & 10 deletions buildout.cfg
@@ -1,28 +1,26 @@
[buildout]
develop = .
parts = test py
parts = test py sphinx

[test]
recipe = zc.recipe.testrunner
eggs = zc.ngi [test]

[test2.4]
recipe = zc.recipe.testrunner
eggs = zc.ngi [test]
python = python2.4

[test2.5]
recipe = zc.recipe.testrunner
eggs = zc.ngi [test]
<= test
python = python2.5

[test2.6]
recipe = zc.recipe.testrunner
eggs = zc.ngi [test]
<= test
python = python2.6

[py]
recipe = zc.recipe.egg
eggs = zc.ngi
interpreter = py

[sphinx]
recipe = zc.recipe.egg
eggs = sphinx
Pygments
zc.ngi
36 changes: 7 additions & 29 deletions setup.py
Expand Up @@ -14,49 +14,27 @@

name, version = 'zc.ngi', '0'

import os
from setuptools import setup, find_packages

def read(*rnames):
return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
readme = open('README.txt').read()

long_description=(
read('README.txt')
+ '\n' +
'Detailed Documentation\n'
'**********************\n'
+ '\n' +
read('src', 'zc', 'ngi', 'README.txt')
+ '\n' +
read('src', 'zc', 'ngi', 'blocking.txt')
+ '\n' +
read('src', 'zc', 'ngi', 'adapters.txt')
+ '\n' +
read('src', 'zc', 'ngi', 'async.txt')
+ '\n' +
'Download\n'
'**********************\n'
)

open('documentation.txt', 'w').write(long_description)
tests_require = ['zope.testing', 'manuel']

setup(
name = name, version=version,
author = "Jim Fulton",
author_email = "jim@zope.com",
description = "Network Gateway Interface",
description = readme.split('\n', 1)[0],
license = "ZPL 2.1",
keywords = "network",
url='http://www.python.org/pypi/'+name,
long_description=long_description,
keywords = ["networking", "testing"],
url='http://packages.python.org/'+name,
long_description=readme,

packages = find_packages('src'),
include_package_data = True,
package_dir = {'':'src'},
namespace_packages = ['zc'],
install_requires = ['setuptools'],
extras_require = dict(
test = ['zope.testing'],
),
extras_require = dict(test=tests_require),
zip_safe = False,
)
71 changes: 48 additions & 23 deletions src/zc/ngi/adapters.py
Expand Up @@ -12,48 +12,64 @@
#
##############################################################################
"""NGI connection adapters
$Id$
"""
import struct
import zc.ngi.generator

class Lines:
class Base(object):

def __init__(self, connection):
self.connection = connection
self.close = connection.close
self.write = connection.write
self.writelines = connection.writelines

def close(self):
self.connection.close()

def write(self, data):
self.write = self.connection.write
self.write(data)

def writelines(self, data):
self.writelines = self.connection.writelines
self.writelines(data)

def setHandler(self, handler):
self.handler = handler
self.input = ''
self.connection.setHandler(self)

def handle_input(self, connection, data):
handle_input = self.handler.handle_input
self.handle_input(connection, data)

def handle_close(self, connection, reason):
self.handler.handle_close(connection, reason)

def handle_exception(self, connection, reason):
self.handler.handle_exception(connection, reason)

@classmethod
def handler(class_, func):
return zc.ngi.generator.handler(func, class_)

class Lines(Base):

input = ''

def handle_input(self, connection, data):
self.input += data
data = self.input.split('\n')
self.input = data.pop()
for line in data:
self.handler.handle_input(self, line)

def handle_close(self, connection, reason):
self.handler.handle_close(self, reason)


class Sized:

def __init__(self, connection):
self.connection = connection
self.close = connection.close
class Sized(Base):

want = 4
got = 0
getting_size = True
def setHandler(self, handler):
self.handler = handler
self.input = []
self.want = 4
self.got = 0
self.getting_size = True
self.connection.setHandler(self)
Base.setHandler(self, handler)

def handle_input(self, connection, data):
self.got += len(data)
Expand Down Expand Up @@ -84,11 +100,20 @@ def handle_input(self, connection, data):
self.getting_size = True
self.handler.handle_input(self, collected)

def handle_close(self, connection, reason):
self.handler.handle_close(self, reason)
def writelines(self, data):
self.connection.writelines(sized_iter(data))

def write(self, message):
if message is None:
self.connection.write('\xff\xff\xff\xff')
else:
self.connection.write(struct.pack(">I", len(message))+message)
self.connection.write(struct.pack(">I", len(message)))
self.connection.write(message)

def sized_iter(data):
for message in data:
if message is None:
yield '\xff\xff\xff\xff'
else:
yield struct.pack(">I", len(message))
yield message
29 changes: 25 additions & 4 deletions src/zc/ngi/adapters.txt
Expand Up @@ -53,10 +53,19 @@ as would the underlying connection:
>>> adapter.write('foo')
-> 'foo'

>>> adapter.writelines(['foo', 'bar'])
-> 'foo'
-> 'bar'
>>> adapter.writelines("%s\n" % foo for foo in range(3))
-> '0\n'
-> '1\n'
-> '2\n'

.. again with feeling

>>> adapter.writelines("%s\n" % foo for foo in range(3))
-> '0\n'
-> '1\n'
-> '2\n'

::
>>> connection.test_close('test')
-> CLOSE test

Expand Down Expand Up @@ -98,7 +107,19 @@ If we write a message, we can see that the message is preceded by the
message size:

>>> adapter.write(message1)
-> '\x00\x00\x00\x19Hello\nWorld!\nHow are you?'
-> '\x00\x00\x00\x19'
-> 'Hello\nWorld!\nHow are you?'

We can give multiple messages using writelines:

>>> adapter.writelines("%s\n" % foo for foo in range(3))
-> '\x00\x00\x00\x02'
-> '0\n'
-> '\x00\x00\x00\x02'
-> '1\n'
-> '\x00\x00\x00\x02'
-> '2\n'


Null messages
-------------
Expand Down

0 comments on commit 6410299

Please sign in to comment.