Commits

imbolc  committed 47a44d9

routing queue

  • Participants
  • Parent commits d599799

Comments (0)

Files changed (6)

 PySi Changelog
 ===============
 
+0.4
+---
+
+- очередь функций роутинга
+- в стандартном роутинге авто-редирект, если забыли слеш
+
+
+
 0.3
 ---
 
 ---
 
 - в url4 можно добавлять дополнительные параметры, которые станут
-    GET аргументами url
+GET аргументами url
     
 - Response теперь бросать исключением.
 Классы Redirect, PermanentRedirect теперь deprecated.

File pysi/config.py

     TEMPLATE_AUTO_RELOAD = False
     TEMPLATE_LOADER = 'pysi.template.file_loader'
     FLASH_COOKIE_NAME = u'pysi.flash'
+    ROUTING = 'pysi.auto_routing'  # функция или очередь функция роутинга
 
     def set_defaults(self, **kwargs):
         '''

File pysi/routing.py

 
 from exceptions import NotFound
 from config import cfg
+import wsgi
 
 
 class UrlMap(object):
     '''
     func, kwargs = urls.find(rq.path)
     if func is None:
+        if not rq.path.endswith('/'):
+            # забыли закрывающий слеш?
+            func, kwargs = urls.find(rq.path + '/')
+            if func:
+                return wsgi.redirect(rq.path + '/')        
         raise NotFound
     return func(rq, **kwargs)
     
-
-
 def add_urls(*rules):
     ''' Добавление правил списком'''
     for rule in rules:
 def url4(*args, **kwargs):
     return urls.reverse(*args, **kwargs)
     
-
 def add_apps(apps):
     '''
     Добавление приложений
         app_name = '%s.%s' % (mod, 'views')
         __import__(app_name)
         urls.url_prefix = None
-            
-            

File pysi/template.py

 # -*- coding: utf-8 -*-
 from os import getcwd
 from os.path import normpath, join, getmtime
+
 try:
     import jinja2
 except ImportError, ex:
             f = open('%s/templates/%s' % (app_name, fn))
         except (IOError, ValueError):
             assert 0, ERR_MSG
-
     path = normpath(join(getcwd(), f.name))
-    mtime = getmtime(path)
     return f.read().decode('utf-8'), path, lambda: mtime == getmtime(path)
 
 def render_to_string(fn, context):
     global _ENV
     try:
-        return _ENV.get_template(fn).render(context)
+        try:
+            return _ENV.get_template(fn).render(context)
+        except EOFError:
+            raise NameError
     except NameError:
         try:
             _ENV = jinja2.Environment(

File pysi/util.py

     '''
     if isinstance(str_or_obj, basestring):
         if '.' not in str_or_obj:
-            str_or_obj += '.'
+            str_or_obj = str_or_obj + '.'
         mod_name, obj_name = str_or_obj.rsplit('.', 1)
         __import__(mod_name)
         mod = sys.modules[mod_name]
         return str_or_obj
 
 def list_obj_from_str(lst):
-    for i in xrange(len(lst)):
-        obj = obj_from_str(lst[i])
-        lst[i] = obj
+    for i, obj in enumerate(lst):
+        lst[i] = obj_from_str(obj)
 
-class cache_property(object):
+class cached_property(object):
     def __init__(self, f):
         self.f = f
 

File pysi/wsgi.py

 from tempfile import TemporaryFile
 from Cookie import SimpleCookie
 
-from util import cache_property, make_traceback, obj_from_str, list_obj_from_str
+from util import cached_property, make_traceback, obj_from_str, list_obj_from_str
 from config import cfg
 from exceptions import NotFound, Redirect, PermanentRedirect, BasicAuth
 from routing import auto_routing, add_apps, url4
         self.environ = environ
         self.path = environ['PATH_INFO'].decode(self.charset, self.encoding_errors)
     
-    @cache_property
+    @cached_property
     def context(self):
         res = {'rq': self, 'url4': url4, 'cfg': cfg}
         for func in cfg.CONTEXT_PROCESSORS:
             res.update(func(self))
         return res
 
-    @cache_property
+    @cached_property
     def method(self):
         return self.environ['REQUEST_METHOD'].upper()
 
-    @cache_property
+    @cached_property
     def GET(self):
         return MultiDict((k.decode(self.charset, self.encoding_errors),
             v[-1].decode(self.charset, self.encoding_errors))
                 for k, v in cgi.parse_qs(self.environ['QUERY_STRING']).iteritems())
         
-    @cache_property
+    @cached_property
     def POST(self):
         '''
         Данные POST-запроса.
                     post[item.name] = item.value.decode(self.charset, self.encoding_errors)
         return post
 
-    @cache_property
+    @cached_property
     def COOKIES(self):
         return SimpleCookie(self.environ.get('HTTP_COOKIE', ''))
 
-    @cache_property
+    @cached_property
     def scheme(self):
         return self.environ.get('HTTP_X_SCHEME',
             self.environ.get('wsgi.url_scheme', 'http'))
 
-    @cache_property
+    @cached_property
     def host(self):
         host = self.environ.get('HTTP_X_FORWARDED_HOST',
             self.environ.get('HTTP_HOST', None))
                 host += ':' + port
         return host
 
-    @cache_property
+    @cached_property
     def full_path(self):
         qs = self.environ.get('QUERY_STRING', '')
         return '%s?%s' % (self.path, qs) if qs else self.path
 
-    @cache_property
+    @cached_property
     def url(self):
         '''Полный урл'''
         return '%s://%s%s' % (self.scheme, self.host, self.full_path)
     
-    @cache_property
+    @cached_property
     def ip(self):
         '''
         Самый внешний ип
         '''
         return self.ip_list[-1]
 
-    @cache_property
+    @cached_property
     def ip_list(self):
         '''
         Список ипов из заголовков проксей за исключением локальных
                 ret.append(ip)
         return ret or [self.environ.get('REMOTE_ADDR')]
 
-    @cache_property
+    @cached_property
     def referer(self):
         return self.environ.get('HTTP_REFERER', '')
         
-    @cache_property
+    @cached_property
     def user_agent(self):
         return self.environ.get('HTTP_USER_AGENT', '')
     
-    @cache_property
+    @cached_property
     def basic_user(self):
         if cfg.BASIC_AUTH_SSL_ONLY and self.scheme != 'https':
             raise redirect('https://%s%s' % (self.host, self.full_path))
             raise BasicAuth
         return user
         
-    @cache_property
+    @cached_property
     def flashes(self):
         ret = []
         if cfg.FLASH_COOKIE_NAME in self.COOKIES:
+            self._clear_flashes = True
             ret = self.COOKIES[cfg.FLASH_COOKIE_NAME].value
-            ret = marshal.loads(base64.urlsafe_b64decode(ret))
-            self._clear_flashes = True
+            try:
+                ret = marshal.loads(base64.urlsafe_b64decode(ret))
+            except (EOFError, ValueError, TypeError):
+                ret = []
         return ret
 
     def flash(self, msg, level='info'):
         
             
 class App(object):
-    def __init__(self, cfg_module=None, routing=None):
+    def __init__(self, cfg_module='cfg'):
         '''
             *cfg_module - модуль конфигурации
-            **routing   - функция роутинга
         '''
-        self.routing = routing or auto_routing
-        cfg_module = obj_from_str(cfg_module)
+        self.cfg_module = cfg_module
+
+
+    def setup(self):
+        '''
+        Первый запуск
+        '''
+        def routing_queue(routing):
+            list_obj_from_str(routing)
+            def wrapper(rq):
+                for func in routing:
+                    try:
+                        return func(rq)
+                    except NotFound:
+                        continue
+                raise NotFound
+            return wrapper
+        
+        cfg_module = obj_from_str(self.cfg_module)
         if cfg_module:
             for k in cfg_module.__dict__.keys():
                 if not k.startswith('__') and k.isupper():
                     setattr(cfg, k, getattr(cfg_module, k))
+
         Request.charset = cfg.CHARSET
         Request.max_post_size = cfg.POST_MAX_SIZE
         Response.charset = cfg.CHARSET
         list_obj_from_str(cfg.REQUEST_MIDDLEWARES)
         list_obj_from_str(cfg.RESPONSE_MIDDLEWARES)
 
+        self.routing = obj_from_str(cfg.ROUTING)
+        if isinstance(self.routing, tuple) or isinstance(self.routing, list):
+            self.routing = routing_queue(self.routing)
+
+        self.Request = Request
+
     def __call__(self, environ, start_response):
-        rq = Request(environ)
+        try:
+            rq = self.Request(environ)
+        except AttributeError:
+            # первый запуск
+            self.setup()
+            rq = self.Request(environ)
         try:
             for middleware in cfg.REQUEST_MIDDLEWARES:
                 middleware(rq)