Skip to content

Commit

Permalink
docs update
Browse files Browse the repository at this point in the history
  • Loading branch information
Eugeny committed Mar 18, 2015
1 parent fbb1ca3 commit e2592e6
Show file tree
Hide file tree
Showing 52 changed files with 467 additions and 451 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
*.whl
*.egg*

plugins/demo*
docs/build
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ rundev:
clean:
find | grep \.pyc | xargs rm


doc:
sphinx-build -b html -d docs/build/doctrees docs/source docs/build/html

cdoc:
rm -rf docs/build/*
make doc


check:
cd plugins && ajenti-dev-multitool --find-outdated

Expand Down
6 changes: 3 additions & 3 deletions ajenti-core/aj/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ def __hash__(self):


__all__ = [
'PluginInfo',
'Context',
'component',
'interface',
'component',
'service',
'Context',
'PluginInfo',
]
51 changes: 51 additions & 0 deletions ajenti-core/aj/api/di.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@


class Context(object):
'''
A dependency injection container for :func:`interface` s, :func:`service` s and :func:`component` s
:param parent: a parent context
:type parent: :class:`Context`
'''

def __init__(self, parent=None):
self.parent = parent
self.service_instances = {}
Expand All @@ -15,6 +22,9 @@ def __repr__(self):

@staticmethod
def get_fqdn(clz):
'''
Returns a fully-qualified name for the given class
'''
return clz.__module__ + '.' + clz.__name__

def get_service(self, cls):
Expand All @@ -32,6 +42,20 @@ def get_components(self, cls):


def service(cls):
'''
Marks the decorated class as a singleton ``service``.
Injects following classmethods:
.. py:function:: get(context)
Returns a singleton instance of the class for given ``context``
:param context: context to look in
:type context: :class:`Context`
:returns: ``cls``
'''

if not cls:
return None

Expand All @@ -46,6 +70,26 @@ def get(cls, context):


def interface(cls):
'''
Marks the decorated class as an abstract interface.
Injects following classmethods:
.. py:function:: all(context)
Returns a list of instances of each component in the ``context`` implementing this ``@interface``
:param context: context to look in
:type context: :class:`Context`
:returns: list(``cls``)
.. py:function:: classes()
Returns a list of classes implementing this ``@interface``
:returns: list(class)
'''

if not cls:
return None

Expand All @@ -66,6 +110,13 @@ def _classes(cls):


def component(iface):
'''
Marks the decorated class as a component implementing the given ``iface``
:param iface: the interface to implement
:type iface: :func:`interface`
'''

def decorator(cls):
if not cls:
return None
Expand Down
74 changes: 50 additions & 24 deletions ajenti-core/aj/api/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def url(pattern):
"""
Exposes the decorated method of your :class:`HttpPlugin` via HTTP
:param pattern: URL regex (no ``^`` and ``$`` required)
:param pattern: URL regex (``^`` and ``$`` are implicit)
:type pattern: str
:rtype: function
Expand All @@ -29,49 +29,48 @@ class BaseHttpHandler(object):
Base class for everything that can process HTTP requests
"""

def handle(self, context):
def handle(self, http_context):
"""
Should create a HTTP response in the given ``context`` and return
Should create a HTTP response in the given ``http_context`` and return
the plain output
:param context: HTTP context
:type context: :class:`aj.http.HttpContext`
:param http_context: HTTP context
:type http_context: :class:`aj.http.HttpContext`
"""


@interface
class HttpPlugin(object):
"""
A base plugin class for HTTP request handling::
@plugin
class TerminalHttp (BasePlugin, HttpPlugin):
@url('/aj:terminal/(?P<id>\\d+)')
def get_page(self, context, id):
if context.session.identity is None:
context.respond_redirect('/')
context.add_header('Content-Type', 'text/html')
A base interface for HTTP request handling::
@component
class HelloHttp(HttpPlugin):
@url('/hello/(?P<name>.+)')
def get_page(self, http_context, name=None):
context.add_header('Content-Type', 'text/plain')
context.respond_ok()
return self.open_content('static/index.html').read()
return 'Hello, %s!' % name
"""

def handle(self, context):
def handle(self, http_context):
"""
Finds and executes the handler for given request context (handlers are
methods decorated with :func:`url` )
:param context: HTTP context
:type context: :class:`aj.http.HttpContext`
:param http_context: HTTP context
:type http_context: :class:`aj.http.HttpContext`
:returns: reponse data
"""

for name, method in self.__class__.__dict__.iteritems():
if hasattr(method, 'url_pattern'):
method = getattr(self, name)
match = method.url_pattern.match(context.path)
match = method.url_pattern.match(http_context.path)
if match:
context.route_data = match.groupdict()
data = method(context, **context.route_data)
http_context.route_data = match.groupdict()
data = method(http_context, **http_context.route_data)
if isinstance(data, types.GeneratorType):
return data
else:
Expand All @@ -80,26 +79,46 @@ def handle(self, context):

@interface
class SocketEndpoint(object):
"""
Base interface for Socket.IO endpoints.
"""

plugin = None
"""arbitrary plugin ID for socket message routing"""

def __init__(self, context):
self.context = context
self.greenlets = []

def on_connect(self, message):
pass
"""
Called on a successful client connection
"""

def on_disconnect(self, message):
pass
"""
Called on a client disconnect
"""

def destroy(self):
"""
Destroys endpoint, killing the running greenlets
"""
for gl in self.greenlets:
gl.kill(block=False)

def on_message(self, message, *args):
pass
"""
Called when a socket message arrives to this endpoint
"""

def spawn(self, target, *args, **kwargs):
"""
Spawns a greenlet in this endpoint, which will be auto-killed when the client disconnects
:param target: target function
"""
logging.debug(
'Spawning sub-Socket Greenlet (in a namespace): %s' % (
target.__name__
Expand All @@ -110,6 +129,13 @@ def spawn(self, target, *args, **kwargs):
return greenlet

def send(self, data, plugin=None):
"""
Sends a message to the client.the
:param data: message object
:param plugin: routing ID (this endpoint's ID if not specified)
:type plugin: str
"""
self.context.worker.send_to_upstream({
'type': 'socket',
'message': {
Expand Down
2 changes: 1 addition & 1 deletion ajenti-core/aj/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def handle(self, http_context):

@public
@service
class AuthenticationService(BaseHttpHandler):
class AuthenticationService(object):
def __init__(self, context):
self.context = context

Expand Down
42 changes: 36 additions & 6 deletions ajenti-core/aj/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ def _validate_origin(env):

class HttpRoot(object):
"""
A root middleware object that creates the :class:`HttpContext` and dispatches
it to other registered middleware
A root WSGI middleware object that creates the :class:`HttpContext` and dispatches
it to an HTTP handler.
:param handler: next middleware handler
:type handler: :class:`aj.api.http.BaseHttpHandler`
"""

def __init__(self, handler):
Expand Down Expand Up @@ -60,6 +63,13 @@ def dispatch(self, env, start_response):


class HttpMiddlewareAggregator(BaseHttpHandler):
"""
Stacks multiple HTTP handlers together in a middleware fashion.
:param stack: handler list
:type stack: list(:class:`aj.api.http.BaseHttpHandler`)
"""

def __init__(self, stack):
self.stack = stack

Expand All @@ -82,10 +92,18 @@ class HttpContext(object):
Path segment of the URL
.. attribute:: method
Request method
.. attribute:: headers
List of HTTP response headers
.. attribute:: body
Request body
.. attribute:: response_ready
Indicates whether a HTTP response has already been submitted in this context
Expand Down Expand Up @@ -163,28 +181,35 @@ def add_header(self, key, value):
"""
Adds a given HTTP header to the response
:type key: str
:type value: str
:param key: header name
:type key: str
:param value: header value
:type value: str
"""
self.headers += [(key, value)]

def remove_header(self, key):
"""
Removed a given HTTP header from the response
:type key: str
:param key: header name
:type key: str
"""
self.headers = [h for h in self.headers if h[0] != key]

def fallthrough(self, handler):
"""
Executes a ``handler`` in this context
:type handler: :class:`aj.api.http.BaseHttpHandler`
:returns: handler-supplied output
"""
return handler.handle(self)

def run_response(self):
"""
Finalizes the response and runs WSGI's ``start_response()``.
"""
if not self.response_ready:
raise Exception('Response not created yet!')

Expand All @@ -199,6 +224,7 @@ def run_response(self):
def respond(self, status):
"""
Creates a response with given HTTP status line
:type status: str
"""
self.status = status
Expand Down Expand Up @@ -234,6 +260,7 @@ def respond_not_found(self):
def redirect(self, location):
"""
Returns a ``HTTP 302 Found`` redirect response with given ``location``
:type location: str
"""
self.add_header('Location', location)
Expand All @@ -243,8 +270,10 @@ def redirect(self, location):
def gzip(self, content, compression=9):
"""
Returns a GZip compressed response with given ``content`` and correct headers
:type content: str
:type compression: int
:param compression: compression level from 0 to 9
:type compression: int
:rtype: str
"""
io = StringIO()
Expand All @@ -262,6 +291,7 @@ def gzip(self, content, compression=9):
def file(self, path, stream=False):
"""
Returns a GZip compressed response with content of file located in ``path`` and correct headers
:type path: str
:type stream: bool
"""
Expand Down

0 comments on commit e2592e6

Please sign in to comment.