Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
alexcepoi committed Feb 7, 2012
0 parents commit 60ca950
Show file tree
Hide file tree
Showing 38 changed files with 1,732 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .gitignore
@@ -0,0 +1,12 @@
# temp files
**pyc
**pyo

# swap files
**swp
**~

# build directory (setup.py)
build/
dist/
**pyscale.egg-info/
2 changes: 2 additions & 0 deletions MANIFEST.in
@@ -0,0 +1,2 @@
include README.rst
graft pyscale/files
91 changes: 91 additions & 0 deletions README.rst
@@ -0,0 +1,91 @@
General purpose Python framework for writing highly scalable applications.

About
---------------------------------------------------
A typical application consists of several modules. Each module has its own
process, stores a pidfile in 'tmp/pids', and has a logfile in 'logs'.

A RPC protocol is implemented on top of zeromq in order to allow for
inter-module communication. Modules have an auto-adjustable number of workers
in order to cope with a high number of requests. These rpc requests will block
until that module becomes available.
Read more about zeromq at http://zguide.zeromq.org/

Each module consists of several gevent greenlets. A basic module will already
contain a few greenlets that handle incoming rpc requests. You can spawn
additional greenlets for your own needs.
Read more about gevent at http://www.gevent.org/


Tasks
---------------------------------------------------
You can manage and debug your modules using built-in tasks. Type 'cake' at a
bash prompt when inside your project to see available tasks and what they do.
You can also define your own taks.

Commands
---------------------------------------------------
To create a new project:

::

$ pyscale new <name>

To generate a new module:

::

$ pyscale generate <name>

To start, stop, debug, view logs and more check out available cake tasks:

::

$ cake
$ cake start
$ cake stop
$ cake status
$ cake log
$ cake console

Usage
---------------------------------------------------
To execute an rpc request on another module:

::

self.sock('modname').method(*args, **kwargs)

You can also use properties, and chain requests:

::

self.sock('modname').prop.method()

You can also issue requests on all available modules:

::

self.multisock('*').method()


To spawn another gevent greenlet in a current module:

::

self.jobs.spawn(func)

To debug your application use logs and the console.

Requirements
---------------------------------------------------
System Dependencies:
* zeromq
* atd

Python Dependencies:
* pyzmq
* gevent
* gevent_zeromq
* cake
* nose
21 changes: 21 additions & 0 deletions bin/pyscale
@@ -0,0 +1,21 @@
#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

import sys

from cake.color import puts, fore
from pyscale.lib import PyscaleError
from pyscale.tools.generators import new, generate


# main
if __name__ == '__main__':
command, arg = sys.argv[1:]

try:
if command in ['new', 'n']:
new(arg)
elif command in ['generate', 'g']:
generate(arg)
except PyscaleError as ex:
puts(fore.red(ex))
Empty file added pyscale/__init__.py
Empty file.
16 changes: 16 additions & 0 deletions pyscale/files/module
@@ -0,0 +1,16 @@
#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

import logging

from lib.module import Module


# Module
class {{module}}(Module):
pass


# main
if __name__ == '__main__':
{{module}}().run()
4 changes: 4 additions & 0 deletions pyscale/files/project/Cakefile
@@ -0,0 +1,4 @@
#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

from pyscale.tools.tasks import *
Empty file.
Empty file.
18 changes: 18 additions & 0 deletions pyscale/files/project/config/app.py
@@ -0,0 +1,18 @@
#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

import os
import os.path as osp
import logging

class Configuration(object):
def __init__(self, env='development'):
self.env = os.environ.get('APP_ENV') or env
self.root = osp.abspath('.')

@property
def log_level(self):
if self.env == 'production':
return logging.WARNING
else:
return logging.DEBUG
Empty file.
18 changes: 18 additions & 0 deletions pyscale/files/project/lib/module.py
@@ -0,0 +1,18 @@
#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

from pyscale.lib.module import BaseModule


class Module(BaseModule):
""" Module Class (daemon) """

# notifications
def notice(self, msg):
pass

def alert(self, msg):
pass

def error(self, msg):
pass
Empty file.
Empty file.
Empty file.
Empty file.
25 changes: 25 additions & 0 deletions pyscale/files/project/tools/console
@@ -0,0 +1,25 @@
#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

import sys

from pyscale.tools.console import main
from pyscale.tools.console import api as _api
from pyscale.tools.console import reinit as _reinit


self = sys.modules[__name__]
self.sockets = []


def api(modules=None):
if modules == None:
modules = self.all

_api(modules)

def reinit(info=True):
_reinit(self, info)


main(self)
5 changes: 5 additions & 0 deletions pyscale/files/project/tools/logger
@@ -0,0 +1,5 @@
#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

from pyscale.tools.logger import main
main()
5 changes: 5 additions & 0 deletions pyscale/lib/__init__.py
@@ -0,0 +1,5 @@
#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

from .decorators import *
from .errors import *
7 changes: 7 additions & 0 deletions pyscale/lib/decorators.py
@@ -0,0 +1,7 @@
#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

def api(method):
""" Basic decorator for module API methods """
method.api = True
return method
40 changes: 40 additions & 0 deletions pyscale/lib/errors.py
@@ -0,0 +1,40 @@
#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-


class PyscaleError(Exception):
def __init__(self, msg):
self.msg = msg

def __str__(self):
return self.msg


class ReqError(Exception):
def __init__(self, msg):
self.msg = msg

def __getattr__(self, key):
return self

def __setattr__(self, key, name):
if key == 'msg':
return super(ReqError, self).__setattr__(key, name)
return self

def __delattr__(self, key):
if key == 'msg':
return super(ReqError, self).__delattr__(key, name)
return self

def __call__(self, *args, **kwargs):
return self

def __str__(self):
return '(error: %s)' % self.msg

def __repr__(self):
return str(self)

def __nonzero__(self):
return False
74 changes: 74 additions & 0 deletions pyscale/lib/log.py
@@ -0,0 +1,74 @@
#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

import sys
import logging
import logging.handlers
import traceback


class MyFormatter(logging.Formatter):
def format(self, record):
record.fname = "[%s:%s]" % (record.filename, record.lineno)
return logging.Formatter.format(self, record)


EXCEPTION_PREFIX = ' => '

def log_exception(msg=None):
""" Custom exception log helper """
if msg is None:
msg = traceback.format_exc()

lines = msg.split('\n')
lines = [ '%s%s' % (EXCEPTION_PREFIX, line) for line in lines if line]
msg = '\n' + '\n'.join(lines) + '\n'

logging.log(75, '\n%s' % msg)

def log_status(msg):
""" Always log regardless of level """
logging.log(100, msg)


def config_logger(stream=sys.stdout, level=logging.DEBUG):
logger = logging.getLogger()
logger.setLevel(level)


# handler
if stream in [sys.stdout, sys.stderr]:
handler = logging.StreamHandler(stream)
else:
handler = logging.handlers.RotatingFileHandler(stream, maxBytes=409600, backupCount=3)
logger.addHandler(handler)

# formatter
formatter = MyFormatter(
fmt = '%(asctime)s %(levelname)-8s %(fname)-20s %(message)s',
datefmt = '%b %d %Y %H:%M:%S',
)
handler.setFormatter(formatter)

# custom levels
logging.addLevelName(75, 'EXCEPT')
logging.exception = log_exception

logging.addLevelName(100, 'STATUS')
logging.status = log_status


# test
if __name__ == '__main__':
config_logger()

logging.warning('test')
logging.info('test')
logging.debug('test')
logging.error('test')

try: err_var
except Exception as ex:
logging.exception()

logging.status('test')

0 comments on commit 60ca950

Please sign in to comment.