[rhmessaging-commits] rhmessaging commits: r3690 - in mgmt/trunk: cumin/bin and 8 other directories.

rhmessaging-commits at lists.jboss.org rhmessaging-commits at lists.jboss.org
Tue Nov 3 12:10:21 EST 2009


Author: justi9
Date: 2009-11-03 12:10:20 -0500 (Tue, 03 Nov 2009)
New Revision: 3690

Added:
   mgmt/trunk/parsley/python/parsley/threadingex.py
   mgmt/trunk/wooly/python/wooly/bench.py
   mgmt/trunk/wooly/python/wooly/profile.py
   mgmt/trunk/wooly/python/wooly/util.py
Removed:
   mgmt/trunk/cumin/python/cumin/user.py
   mgmt/trunk/wooly/python/wooly/devel.py
Modified:
   mgmt/trunk/basil/python/basil/server.py
   mgmt/trunk/cumin/bin/cumin-bench
   mgmt/trunk/cumin/python/cumin/account/model.py
   mgmt/trunk/cumin/python/cumin/account/widgets.py
   mgmt/trunk/cumin/python/cumin/grid/job.py
   mgmt/trunk/cumin/python/cumin/grid/main.py
   mgmt/trunk/cumin/python/cumin/grid/pool.py
   mgmt/trunk/cumin/python/cumin/inventory/system.py
   mgmt/trunk/cumin/python/cumin/main.py
   mgmt/trunk/cumin/python/cumin/model.py
   mgmt/trunk/cumin/python/cumin/parameters.py
   mgmt/trunk/cumin/python/cumin/server.py
   mgmt/trunk/cumin/python/cumin/tools.py
   mgmt/trunk/cumin/python/cumin/util.py
   mgmt/trunk/cumin/python/cumin/widgets.py
   mgmt/trunk/cumin/python/cumin/widgets.strings
   mgmt/trunk/mint/python/mint/main.py
   mgmt/trunk/mint/python/mint/model.py
   mgmt/trunk/misc/boneyard.py
   mgmt/trunk/wooly/python/wooly/__init__.py
   mgmt/trunk/wooly/python/wooly/demo.py
   mgmt/trunk/wooly/python/wooly/pages.py
   mgmt/trunk/wooly/python/wooly/pages.strings
   mgmt/trunk/wooly/python/wooly/server.py
   mgmt/trunk/wooly/python/wooly/sql.py
Log:
The bigger

 * Refactor widget call tracing, and introduce a PageProfile object
   for collecting trace info

 * Revamp qpid-bench for producing profiles; for now, it's only the
   render half

 * Add "client sessions", a generic server-side per-user session
   facility; for web login, we associate a login session with a client
   session

 * Simplify the web server code, and add client session support

The smaller

 * Fix a page crash from a failure to call super.do_process in
   SettingsView

 * Temporarily disable the job links from the system slots view; it
   was just a stub, and a stub that was causing a page crash

 * For now, take the last_pool stuff out until I replace it with a
   client_session attribute

 * Turn off task enter/exit logging; it was too verbose

 * Eliminate the get/set_redirect_url methods in favor of using the
   attribute directly

 * Add utility functions for printing threads and their state

 * Add a Lifecycle class to represent any object having init, start,
   and stop phases; this eliminates a lot of duplicate logging code

 * Use the app-level devel_enabled attr to control debug and profiling

 * Add some utility sorting functions

 * Remove the unused DevelPage


Modified: mgmt/trunk/basil/python/basil/server.py
===================================================================
--- mgmt/trunk/basil/python/basil/server.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/basil/python/basil/server.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -23,8 +23,6 @@
 
         self.add_resource_dir("/home/boston/jross/local/mgmt/cumin/instance/resources-wooly") # XXX
 
-        self.enable_debug()
-
     def init(self):
         super(BasilApplication, self).init()
 

Modified: mgmt/trunk/cumin/bin/cumin-bench
===================================================================
--- mgmt/trunk/cumin/bin/cumin-bench	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/bin/cumin-bench	2009-11-03 17:10:20 UTC (rev 3690)
@@ -1,6 +1,6 @@
 #!/usr/bin/python
 
-import sys, os
+import sys
 
 from cumin.tools import CuminBenchTool
 
@@ -56,3 +56,6 @@
         main()
     except KeyboardInterrupt:
         pass
+
+        #from parsley import threadingex
+        #threadingex.print_threads()

Modified: mgmt/trunk/cumin/python/cumin/account/model.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/account/model.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/python/cumin/account/model.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -1,5 +1,5 @@
 from cumin.model import *
-from cumin.user import *
+from cumin.util import *
 
 from widgets import *
 
@@ -18,3 +18,10 @@
     def do_invoke(self, session, subject, password):
         subject.password = crypt_password(password)
         subject.syncUpdate()
+
+class LoginSession(object):
+    def __init__(self, app, user):
+        self.app = app
+        self.user = user
+
+        self.created = datetime.now()

Modified: mgmt/trunk/cumin/python/cumin/account/widgets.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/account/widgets.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/python/cumin/account/widgets.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -5,11 +5,11 @@
 from wooly.resources import *
 
 from cumin import *
-from cumin.user import *
 from cumin.widgets import *
 from cumin.util import *
 
 import main
+import model
 
 from wooly import Session
 
@@ -53,6 +53,8 @@
         link.html_class = "action"
         self.add_child(link)
 
+        super(SettingsView, self).init()
+
 class LoginPage(HtmlPage):
     def __init__(self, app, name):
         super(LoginPage, self).__init__(app, name)
@@ -70,8 +72,10 @@
 
     def do_process(self, session):
         if self.logout.get(session):
-            #id = session.get_cookie("session")
-            session.expire_cookie("session")
+            try:
+                del session.client_session.attributes["login_session"]
+            except KeyError:
+                pass
 
         super(LoginPage, self).do_process(session)
 
@@ -119,12 +123,12 @@
                 if crypted and crypt(password, crypted) == crypted:
                     # You're in!
 
-                    usess = UserSession(self.app, user)
-                    session.set_cookie("session", usess.id)
+                    login = model.LoginSession(self.app, user)
+                    session.client_session.attributes["login_session"] = login
 
                     url = self.page.origin.get(session)
 
-                    self.page.set_redirect_url(session, url)
+                    self.page.redirect.set(session, url)
                 else:
                     self.login_invalid.set(session, True)
 

Modified: mgmt/trunk/cumin/python/cumin/grid/job.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/job.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/python/cumin/grid/job.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -186,7 +186,7 @@
                     first = rows[0]
                     job = Identifiable(first["id"])
                     href = self.page.main.grid.pool.job.get_href(session, job)
-                    self.page.set_redirect_url(session, href)
+                    self.page.redirect.set(session, href)
                 except:
                     self.job_search.set_not_found(session, search_term)
             else:
@@ -450,7 +450,8 @@
         job = self.job.get(session)
         cls = self.app.model.get_class_by_object(job)
 
-        job_ads = self.get_raw_ads(session, job)
+        # XXX
+        job_ads = self.get_raw_ads(session, job, None)
 
         return [self.gen_item(x, job_ads[x]["VALUE"], cls,
                               dtype=self.types[job_ads[x]["TYPE"]])
@@ -468,6 +469,8 @@
         if scheduler:
             action = self.app.model.scheduler.GetAd
             return action.qmfcall(scheduler, job.id)
+        else:
+            return () # XXX
 
     def gen_items(self, session, job, scheduler):
         job_ads = self.get_raw_ads(session, job, scheduler)
@@ -661,7 +664,7 @@
         branch = session.branch()
         self.ads.set(branch, None) # otherwise url is too long
         self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
+        self.page.redirect.set(session, branch.marshal())
 
     def process_submit(self, session):
         ads = self.ads.get(session)
@@ -782,7 +785,7 @@
         err_file = None
 
         scheduler = self.frame.scheduler.get(session)
-        ads = self.ads.do_get_items(session, job, scheduler)
+        ads = self.ads.do_get_items(session)
         for ad in ads:
             if ad['name'] == "Out":
                 out_file = ad['value']

Modified: mgmt/trunk/cumin/python/cumin/grid/main.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/main.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/python/cumin/grid/main.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -76,15 +76,6 @@
         self.pool = PoolFrame(app, "pool")
         self.add_mode(self.pool)
 
-        self.sticky_pool = Parameter(app, "last_pool")
-        self.add_parameter(self.sticky_pool)
-
-    def set_last_pool(self, session, pool):
-        self.sticky_pool.set(session, pool)
-
-    def get_last_pool(self, session):
-        return self.sticky_pool.get(session)
-
     def render_title(self, session):
         return "Grid"
 

Modified: mgmt/trunk/cumin/python/cumin/grid/pool.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/pool.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/python/cumin/grid/pool.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -55,7 +55,6 @@
 
         def render_content(self, session, data):
             pool = Identifiable(data["id"])
-            self.page.main.grid.set_last_pool(session, data["id"])
             href = self.page.main.grid.pool.get_href(session, pool)
 
             return fmt_link(href, data["name"])

Modified: mgmt/trunk/cumin/python/cumin/inventory/system.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/inventory/system.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/python/cumin/inventory/system.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -150,8 +150,9 @@
         return "Statistics"
 
     def render_slot_job_url(self, session):
-        job = Identifiable("XXX")
-        return self.page.main.grid.pool.job.get_href(session, job, None)
+        #job = Identifiable("XXX")
+        #return self.page.main.grid.pool.job.get_href(session, job, None)
+        return session.marshal() # XXX
 
 class SystemSlotMap(SlotMap):
     def __init__(self, app, name, system):

Modified: mgmt/trunk/cumin/python/cumin/main.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/main.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/python/cumin/main.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -8,7 +8,6 @@
 from stat import StatChartPage, StatStackedPage, \
     StatFlashPage, FlashFullPage
 from wooly import Application, Session, Page
-from wooly.devel import DevelPage
 from wooly.pages import ResourcePage
 from wooly.parameters import IntegerParameter
 
@@ -35,10 +34,11 @@
     def __init__(self, config):
         super(Cumin, self).__init__()
 
+        self.log = log
+
         self.config = config
 
-        if self.config.debug:
-            self.enable_debug()
+        self.devel_enabled = self.config.debug
 
         self.modules = list()
 
@@ -63,7 +63,6 @@
         self.account_page = account.AccountPage(self, "account.html")
         self.add_page(self.account_page)
 
-        self.add_page(DevelPage(self, "devel.html"))
         self.add_page(CallPage(self, "call.xml"))
         self.add_page(StatChartPage(self, "stats.png"))
         self.add_page(StatStackedPage(self, "stacked.png"))
@@ -75,9 +74,6 @@
         self.login_page.javascript_page.protected = False
         self.resource_page.protected = False
 
-        self.user_sessions_by_id = dict()
-        self.user_session_expire_thread = UserSessionExpireThread(self)
-
     def check(self):
         if not os.path.isdir(self.home):
             raise Exception \
@@ -86,7 +82,7 @@
 
         self.model.check()
 
-    def init(self):
+    def do_init(self):
         self.modules.append(account.module)
         self.modules.append(messaging.module)
         self.modules.append(grid.module)
@@ -98,13 +94,12 @@
 
         self.model.init()
 
-        super(Cumin, self).init()
+        super(Cumin, self).do_init()
 
-    def start(self):
+    def do_start(self):
         self.model.start()
-        self.user_session_expire_thread.start()
 
-    def stop(self):
+    def do_stop(self):
         self.model.stop()
 
 class MainPage(CuminPage, ModeSet):

Modified: mgmt/trunk/cumin/python/cumin/model.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/model.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/python/cumin/model.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -207,7 +207,7 @@
         return self.enter(session, object).marshal()
 
     def enter(self, session, object):
-        log.debug("Entering %s", self)
+        #log.debug("Entering %s", self)
 
         nsession = wooly.Session(self.app.form_page)
 
@@ -216,7 +216,7 @@
 
         self.do_enter(nsession, object)
 
-        log.info("Entered %s", self)
+        #log.info("Entered %s", self)
 
         return nsession
 
@@ -225,7 +225,7 @@
 
     def exit_with_redirect(self, session, object):
         osession = self.exit(session, object)
-        self.form.page.set_redirect_url(session, osession.marshal())
+        self.form.page.redirect.set(session, osession.marshal())
 
     def exit(self, session, object):
         log.debug("Exiting %s", self)

Modified: mgmt/trunk/cumin/python/cumin/parameters.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/parameters.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/python/cumin/parameters.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -6,8 +6,10 @@
 
 class UserAttribute(Attribute):
     def get(self, session):
-        return session.user_session.subject
+        login = session.client_session.attributes["login_session"]
 
+        return login.user
+
 class ObjectAssociateAttribute(Attribute):
     def __init__(self, app, name, object):
         super(ObjectAssociateAttribute, self).__init__(app, name)

Modified: mgmt/trunk/cumin/python/cumin/server.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/server.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/python/cumin/server.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -1,14 +1,7 @@
-import logging
+# XXX get rid of this
 
-from datetime import datetime, timedelta
-from wooly import *
 from wooly.server import WebServer
 
-from mint import Subject
-from cumin import UserSession
-
-log = logging.getLogger("cumin.server")
-
 class CuminServer(WebServer):
     def authorized(self, session):
         return True

Modified: mgmt/trunk/cumin/python/cumin/tools.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/tools.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/python/cumin/tools.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -1,12 +1,14 @@
 import sys, os, re, signal
 
+from mint import *
 from parsley.config import *
 from parsley.command import *
-from wooly.devel import *
+from wooly.bench import *
 from time import sleep
 
 from util import *
 from cumin import Cumin
+from cumin.account.model import *
 from cumin.config import CuminConfig
 from cumin.server import CuminServer
 from test import *
@@ -194,16 +196,22 @@
 
         self.description = "Cumin benchmarking tool"
 
-        opt = CommandOption(self, "hits")
+        opt = CommandOption(self, "count")
         opt.argument = "COUNT"
-        opt.description = "Stop after COUNT page hits"
+        opt.description = "Stop after COUNT page hits; 0 means no limit"
 
         opt = CommandOption(self, "profile")
         opt.description = "Enable profiling"
 
-        opt = CommandOption(self, "check-xml")
+        opt = CommandOption(self, "continue-on-error")
+        opt.description = "Don't stop if a page load fails"
+
+        opt = CommandOption(self, "check-output")
         opt.description = "Check that page output is well-formed xml"
 
+        opt = CommandOption(self, "print-output")
+        opt.description = "Print the rendered page to the console"
+
         opt = CommandOption(self, "broker")
         opt.argument = "ADDR"
         opt.description = "Use existing broker at ADDR"
@@ -219,12 +227,20 @@
             sys.exit(1)
 
         app.init()
+        app.start()
 
-        harness = BenchmarkHarness(app, "check-xml" in opts)
+        harness = BenchmarkHarness(app)
+        harness.continue_on_error = "continue-on-error" in opts
+        harness.check_output = "check-output" in opts
+        harness.print_output = "print-output" in opts
 
-        app.start()
+        login = LoginSession(app, Subject.getByName("guest"))
 
+        harness.client_session.attributes["login_session"] = login
+
+        sleep(0.5) # XXXX!  arrgh
+
         try:
-            harness.run(int(opts.get("hits", "1000")))
+            harness.run(int(opts.get("count", "1000")))
         finally:
             app.stop()

Deleted: mgmt/trunk/cumin/python/cumin/user.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/user.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/python/cumin/user.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -1,38 +0,0 @@
-from util import *
-
-log = logging.getLogger("cumin.user")
-
-class UserSession(object):
-    def __init__(self, app, subject):
-        self.app = app
-        self.subject = subject
-        self.id = str(uuid4())
-        self.created = datetime.now()
-
-        self.app.user_sessions_by_id[self.id] = self
-
-    def delete(self):
-        del self.app.user_sessions_by_id[self.id]
-
-class UserSessionExpireThread(Thread):
-    def __init__(self, app):
-        super(UserSessionExpireThread, self).__init__()
-
-        self.app = app
-        self.setDaemon(True)
-
-    def run(self):
-        while True:
-            self.expire_sessions()
-            sleep(60)
-
-    def expire_sessions(self):
-        when = datetime.now() - timedelta(hours=2)
-        count = 0
-
-        for session in self.app.user_sessions_by_id.values():
-            if session.created < when:
-                session.delete()
-                count += 1
-
-        log.info("Expired %i user sessions", count)

Modified: mgmt/trunk/cumin/python/cumin/util.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/util.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/python/cumin/util.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -15,8 +15,14 @@
     return "%08x" % randint(0, sys.maxint)
 
 def sorted_by(seq, attr="name"):
+    return sorted_by_attr(seq, attr)
+
+def sorted_by_attr(seq, attr):
     return sorted(seq, cmp, lambda x: getattr(x, attr))
 
+def sorted_by_index(seq, index):
+    return sorted(seq, cmp, lambda x: x[index])
+
 def ess(num, ending="s"):
     return num != 1 and ending or ""
 

Modified: mgmt/trunk/cumin/python/cumin/widgets.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/widgets.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/python/cumin/widgets.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -22,6 +22,10 @@
 
 strings = StringCatalog(__file__)
 
+class CuminSqlDataSet(SqlDataSet):
+    def get_connection(self, session):
+        return self.app.model.get_sql_connection()
+
 class CuminHeartBeat(Widget):
     """ the intent is to add stuff here """
     pass
@@ -72,16 +76,27 @@
         super(CuminPageLinks, self).__init__(app, name)
 
         self.html_class = CuminPageLinks.__name__
-        
+
+        self.check_admin = self.CheckAdmin(app)
+
     def do_get_items(self, session):
         pages = list()
+
         user = self.page.user.get(session)
 
-        for role in user.roles:
-            if role.name == "admin":
-                pages.append(self.app.main_page)
-                break
+        # if user is admin, display main_page XXX
 
+        #self.check_admin.add_where_expr \
+        #    (session, "r.name = 'admin'")
+        #self.check_admin.add_where_expr \
+        #    (session, "m.subject_id = %i", user.id)
+
+        #results = self.check_admin.execute(session)
+
+        #if results.fetchone():
+        #    pages.append(self.app.main_page)
+
+        pages.append(self.app.main_page)
         pages.append(self.app.user_grid_page)
         pages.append(self.app.account_page)
 
@@ -99,6 +114,9 @@
         else:
             return "_"
 
+    class CheckAdmin(CuminSqlDataSet):
+        pass
+
 class CuminFrame(Frame, ModeSet):
     def __init__(self, app, name):
         super(CuminFrame, self).__init__(app, name)
@@ -729,10 +747,6 @@
 
         self.paginator.set_count(session, self.get_item_count(session, *args))
 
-class CuminSqlDataSet(SqlDataSet):
-    def get_connection(self, session):
-        return self.app.model.get_sql_connection()
-
 class CuminTable(SqlTable):
     def __init__(self, app, name):
         super(CuminTable, self).__init__(app, name)
@@ -1309,32 +1323,19 @@
         if not self.protected:
             return True
 
-        id = session.get_cookie("session")
+        login = session.client_session.attributes.get("login_session")
 
-        if id is not None:
-            usess = self.app.user_sessions_by_id.get(id)
+        if login:
+            when = datetime.now() - timedelta(hours=24)
 
-            if usess is not None:
-                timeout = timedelta(seconds=3600)
-                now = datetime.now()
+            if login.created > when:
+                return True
+            
+        # XXX
+        # if self.app.config.user:
+        #     user = Subject.getByName(self.app.config.user)
+        #     assert user
 
-                if now > usess.created and now < usess.created + timeout:
-                    setattr(session, "user_session", usess)
-
-                    return True
-
-        if self.app.config.user:
-            user = Subject.getByName(self.app.config.user)
-
-            assert user
-
-            usess = UserSession(self.app, user)
-            session.set_cookie("session", usess.id)
-
-            self.redirect.set(session, session.marshal())
-
-            return True
-
         lpage = self.app.login_page
 
         lsess = Session(lpage)
@@ -1344,10 +1345,6 @@
 
         return False
 
-    def save_session(self, session):
-        if self.app.debug:
-            self.app.debug.sessions.append(session)
-
 class CuminFormPage(CuminPage):
     def __init__(self, app, name):
         super(CuminFormPage, self).__init__(app, name)
@@ -1467,7 +1464,7 @@
         items = self.set_param.get(session)
 
         href = self.task.get_href(session, items)
-        self.page.set_redirect_url(session, href)
+        self.page.redirect.set(session, href)
 
     def render_content(self, session):
         return self.task.get_title(session)

Modified: mgmt/trunk/cumin/python/cumin/widgets.strings
===================================================================
--- mgmt/trunk/cumin/python/cumin/widgets.strings	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/cumin/python/cumin/widgets.strings	2009-11-03 17:10:20 UTC (rev 3690)
@@ -196,6 +196,12 @@
     color: black;
 }
 
+[CheckAdmin.sql]
+select 1
+from subject_role_mapping as m
+join role as r on m.role_id = r.id
+{sql_where}
+
 [CuminHeartBeat.css]
 #updateStatus {
     float:right;

Modified: mgmt/trunk/mint/python/mint/main.py
===================================================================
--- mgmt/trunk/mint/python/mint/main.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/mint/python/mint/main.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -4,6 +4,7 @@
 
 from parsley.config import Config, ConfigParameter
 from parsley.loggingex import enable_logging
+from parsley.threadingex import Lifecycle
 
 from database import MintDatabase
 from model import MintModel
@@ -13,8 +14,10 @@
 
 log = logging.getLogger("mint.main")
 
-class Mint(object):
+class Mint(Lifecycle):
   def __init__(self, config):
+    self.log = log
+
     self.config = config
     self.database = MintDatabase(self)
     self.model = MintModel(self)
@@ -34,7 +37,7 @@
     self.database.check()
     self.model.check()
 
-  def init(self):
+  def do_init(self):
     self.database.init()
     self.model.init()
 
@@ -49,7 +52,7 @@
     self.pollThread.init()
     self.expireThread.init()
 
-  def start(self):
+  def do_start(self):
     self.model.start()
 
     if self.updateEnabled:
@@ -61,7 +64,7 @@
     if self.expireEnabled:
       self.expireThread.start()
 
-  def stop(self):
+  def do_stop(self):
     self.model.stop()
 
     if self.updateEnabled:

Modified: mgmt/trunk/mint/python/mint/model.py
===================================================================
--- mgmt/trunk/mint/python/mint/model.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/mint/python/mint/model.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -7,6 +7,7 @@
 import types
 import socket
 
+from parsley.threadingex import Lifecycle
 from qmf.console import ClassKey
 from qpid.datatypes import UUID
 from qpid.util import URL
@@ -129,10 +130,12 @@
   def getFullUrl(self):
     return self.qmfBroker.getFullUrl()
 
-class MintModel(qmf.console.Console):
+class MintModel(qmf.console.Console, Lifecycle):
   staticInstance = None
 
   def __init__(self, app):
+    self.log = log
+
     assert MintModel.staticInstance is None
     MintModel.staticInstance = self
 
@@ -166,7 +169,7 @@
   def check(self):
     pass
 
-  def init(self):
+  def do_init(self):
     assert self.qmfSession is None
 
     self.qmfSession = qmf.console.Session \
@@ -179,14 +182,14 @@
     up = update.AgentDisconnectUpdate(self, 0)
     self.app.updateThread.enqueue(up)
 
-  def start(self):
+  def do_start(self):
     uris = [x.strip() for x in self.app.config.qmf.split(",")]
 
     for uri in uris:
       self.addBroker(uri)
 
-  def stop(self):
-    for mbroker in self.mintBrokersById.values():
+  def do_stop(self):
+    for mbroker in self.mintBrokersByQmfBroker.values():
       self.delBroker(mbroker)
 
   def callMethod(self, brokerId, objId, classKey, methodName, callback, args):

Modified: mgmt/trunk/misc/boneyard.py
===================================================================
--- mgmt/trunk/misc/boneyard.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/misc/boneyard.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -188,7 +188,7 @@
     def process_cancel(self, session, prop):
         branch = session.branch()
         self.page.show_broker(branch, prop.get_broker()).view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
+        self.page.redirect.set(session, branch.marshal())
 
     def process_submit(self, session, prop):
         source = self.source.get(session)

Added: mgmt/trunk/parsley/python/parsley/threadingex.py
===================================================================
--- mgmt/trunk/parsley/python/parsley/threadingex.py	                        (rev 0)
+++ mgmt/trunk/parsley/python/parsley/threadingex.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -0,0 +1,68 @@
+import os
+import sys
+
+from threading import *
+
+def print_threads(writer=sys.stdout):
+    row = "%-18s  %-18s  %-18s  %-18s"
+
+    writer.write(row % ("Name", "Ident", "Alive", "Daemon"))
+    writer.write(os.linesep)
+    writer.write("-" * 80)
+    writer.write(os.linesep)
+
+    for thread in enumerate():
+        name = thread.name
+        ident = thread.ident
+        alive = thread.is_alive()
+        daemon = thread.daemon
+        
+        writer.write(row % (name, ident, alive, daemon))
+        writer.write(os.linesep)
+
+class Lifecycle(object):
+    def __init__(self):
+        super(Lifecycle, self)
+
+        self.log = None
+
+    def init(self):
+        if self.log:
+            self.log.debug("Initializing %s" % self)
+
+        self.do_init()
+
+        if self.log:
+            self.log.info("Initialized %s" % self)
+        
+    def do_init(self):
+        pass
+
+    def start(self):
+        if self.log:
+            self.log.debug("Starting %s" % self)
+
+        self.do_start()
+
+        if self.log:
+            self.log.info("Started %s" % self)
+        
+    def do_start(self):
+        pass
+
+    def stop(self):
+        if self.log:
+            self.log.debug("Stopping %s" % self)
+
+        self.do_stop()
+
+        if self.log:
+            self.log.info("Stopped %s" % self)
+
+        #print_threads()
+        
+    def do_stop(self):
+        pass
+
+    def __str__(self):
+        return self.__class__.__name__

Modified: mgmt/trunk/wooly/python/wooly/__init__.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/__init__.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/wooly/python/wooly/__init__.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -1,13 +1,18 @@
-import sys, os, logging
+import logging
+import os
+import sys
+
+from parsley.threadingex import Lifecycle
 from cStringIO import StringIO
 from urllib import quote, unquote_plus, unquote
 from copy import copy
-from time import clock
-from datetime import datetime
 from urlparse import urlsplit
 from time import gmtime
+from uuid import uuid4
 
+from profile import *
 from resources import ResourceFinder, StringCatalog
+from util import *
 
 strings = StringCatalog(__file__)
 
@@ -181,9 +186,6 @@
         self.path = ".".join(pelems[1:])
 
         self.page = self.ancestors[-1]
-
-        self.test(isinstance(self.page, Page))
-
         self.page.init_widget(self)
 
         self.sealed = True
@@ -200,6 +202,14 @@
         for child in self.children:
             child.init()
 
+    def check_me_merrily(self):
+        self.test(self.sealed)
+        self.test(self.path is not None)
+        self.test(isinstance(self.page, Page))
+
+        for child in self.children:
+            child.check_me_merrily()
+
     def add_child(self, child):
         self.test(not self.sealed)
         self.test(isinstance(child, Widget))
@@ -273,50 +283,40 @@
         return ()
 
     def process(self, session):
-        if session.debug:
-            call = WidgetCall(session.debug.process_stack,
-                              self, session, None)
-            call.open()
-            session.debug.processed(self)
+        if self.app.devel_enabled:
+            profile = self.page.profile.get(session)
 
-        if self.update_enabled:
-            self.page.enable_update(session, self)
+            call = ProcessCall(profile, self)
+            call.do(session)
+        else:
+            # XXX these should move into do_process
 
-        if self.defer_enabled:
-            self.page.enable_defer(session, self)
+            if self.update_enabled:
+                self.page.enable_update(session, self)
 
-        args = self.get_args(session)
-        self.do_process(session, *args)
+            if self.defer_enabled:
+                self.page.enable_defer(session, self)
 
-        if session.debug:
-            call.close()
+            args = self.get_args(session)
+            self.do_process(session, *args)
 
     def do_process(self, session, *args):
         for child in self.children:
             child.process(session)
 
     def render(self, session):
-        if session.debug:
-            call = WidgetCall(session.debug.render_stack,
-                              self, session, None)
-            call.open()
-            session.debug.rendered(self)
+        if self.app.devel_enabled:
+            profile = self.page.profile.get(session)
 
-        args = self.get_args(session)
-        string = self.do_render(session, *args)
+            call = RenderCall(profile, self)
+            string = call.do(session)
+        else:
+            args = self.get_args(session)
+            string = self.do_render(session, *args)
 
-        if string is None:
-            string = ""
+            if string is None:
+                string = ""
 
-        if session.debug:
-            call.close()
-
-            if self.parent:
-                cls = self.__class__
-                name = self.__repr__()
-                string = "<!-- \nopen: %s\n -->%s<!-- \nclose: %s\n -->" % \
-                    (name, string, name)
-
         return string
 
     def do_render(self, session, *args):
@@ -394,6 +394,9 @@
         self.redirect = Attribute(app, "redirect")
         self.add_attribute(self.redirect)
 
+        self.profile = self.ProfileAttribute(app, "profile")
+        self.add_attribute(self.profile)
+
     def init(self):
         self.test(not self.sealed)
         self.test(self.parent is None)
@@ -454,23 +457,16 @@
     def enable_update(self, session, widget):
         pass
 
-    def set_redirect_url(self, session, url):
-        self.redirect.set(session, url)
-
-        return url
-
-    def get_redirect_url(self, session):
-        return self.redirect.get(session)
-
-    def set_agent(self, session, agent):
-        self.agent.set(session, agent)
-
     def render_id(self, session, *args):
         return self.name
 
+    class ProfileAttribute(Attribute):
+        def get_default(self, session):
+            return PageProfile(self)
+
 from parameters import DictParameter
 
-class Application(object):
+class Application(Lifecycle):
     def __init__(self):
         self.pages = list()
         self.pages_by_name = dict()
@@ -480,21 +476,13 @@
 
         self.finder = ResourceFinder()
 
-        self.debug = None
+        self.devel_enabled = False
 
-    def enable_debug(self):
-        self.debug = self.Debug(self)
-
-    def init(self):
+    def do_init(self):
         for page in self.pages:
             page.init()
+            page.check_me_merrily()
 
-    class Debug(object):
-        def __init__(self, app):
-            self.app = app
-            self.sessions = list()
-            self.urls = set()
-
     def add_page(self, page):
         if page.parent:
             raise Exception("Page '%s' is not a root widget" % page.name)
@@ -526,76 +514,19 @@
 
         self.app = page.app
         self.page = page
+
+        self.client_session = None
+
         self.trunk = None
 
         self.values_by_path = dict()
         self.cookies_by_name = dict() # name => (newly set?, value, expires)
         self.headers_by_name = dict()
 
-        if self.app.debug:
-            self.debug = self.Debug(self)
-        else:
-            self.debug = None
-
-    class Debug(object):
-        def __init__(self, session):
-            self.session = session
-            self.process_stack = list()
-            self.render_stack = list()
-            self.processed_list = list()
-            self.rendered_list = list()
-
-        def write(self, writer):
-            writer.write(str(self.session))
-            writer.write(os.linesep)
-
-            for item in sorted(self.session.values_by_path.items()):
-                writer.write("  %s = %s" % item)
-                writer.write(os.linesep)
-
-            if self.process_stack:
-                writer.write(os.linesep)
-                writer.write("process trace:")
-                writer.write(os.linesep)
-
-                for call in self.process_stack:
-                    writer.write("  ")
-                    call.write(writer)
-                    writer.write(os.linesep)
-
-            if self.render_stack:
-                writer.write(os.linesep)
-                writer.write("render trace:")
-                writer.write(os.linesep)
-
-                for call in self.render_stack:
-                    writer.write("  ")
-                    call.write(writer)
-                    writer.write(os.linesep)
-
-        def print_last_call(self, out=sys.stdout):
-            if self.render_stack:
-                self.render_stack[-1].write(out)
-            elif self.process_stack:
-                self.process_stack[-1].write(out)
-
-        def processed(self, widget):
-            self.processed_list.append(widget)
-
-        def rendered(self, widget):
-            self.rendered_list.append(widget)
-
-        def check(self, out=sys.stdout):
-            problems = list()
-            for widget in self.rendered_list:
-                if not widget in self.processed_list:
-                    problems.append(widget)
-
-            return problems
-
     def branch(self):
         session = Session(self.page)
         session.trunk = self
+
         return session
 
     def get_cookie(self, name):
@@ -642,8 +573,9 @@
         else:
             url = page
 
-        if self.debug:
-            self.app.debug.urls.add(url)
+        if self.app.devel_enabled: 
+            profile = self.page.profile.get(self)
+            profile.urls.append(url)
 
         return url
 
@@ -761,9 +693,17 @@
             self.cookies_by_name[name.strip()] = (False, value.strip(), None)
 
     def __repr__(self):
-        return "%s(trunk=%s,app=%s)" % \
-            (self.__class__.__name__, self.trunk, self.app)
+        return "%s(trunk=%s,app=%s,id=%i)" % \
+            (self.__class__.__name__, self.trunk, self.app, id(self))
 
+class ClientSession(object):
+    def __init__(self):
+        self.id = str(uuid4())
+        self.created = datetime.now()
+        self.visited = None
+
+        self.attributes = dict()
+
 class StringIOWriter(object):
     def __init__(self):
         self.writer = StringIO()
@@ -911,36 +851,4 @@
 class Template(WidgetTemplate):
     pass
 
-class WidgetCall(object):
-    def __init__(self, stack, widget, session, object):
-        self.stack = stack
-        self.widget = widget
-        self.session = session
-        self.session_values = copy(session.values_by_path)
-
-        self.caller = None
-        self.callees = list()
-
-        self.start = None
-        self.end = None
-
-    def open(self):
-        if self.stack:
-            self.caller = self.stack[-1]
-            self.caller.callees.append(self)
-
-        self.stack.append(self)
-
-        self.start = clock()
-
-    def close(self):
-        self.end = clock()
-
-        #if len(self.stack) > 1:
-        self.stack.pop()
-
-    def write(self, writer):
-        writer.write(str(self.widget))
-        writer.write(" [%i]" % id(self))
-
 from pages import ResourcePage

Added: mgmt/trunk/wooly/python/wooly/bench.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/bench.py	                        (rev 0)
+++ mgmt/trunk/wooly/python/wooly/bench.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -0,0 +1,163 @@
+import os
+import sys
+
+from collections import defaultdict
+from traceback import print_exc
+from time import time
+
+from wooly import Session, ClientSession
+
+class BenchmarkHarness(object):
+    def __init__(self, app):
+        self.app = app
+        self.continue_on_error = False
+        self.check_output = False
+        self.print_output = False
+
+        from wooly import ClientSession, Session
+        self.client_session = ClientSession()
+
+        self.profiles = list()
+
+    def visit(self, url, referer, depth):
+        if depth > 2:
+            raise Exception("Too many redirects")
+
+        session = Session.unmarshal(self.app, url)
+        session.client_session = self.client_session
+
+        page = session.page
+
+        profile = page.profile.get(session)
+        self.profiles.append(profile)
+
+        try:
+            page.process(session)
+
+            redirect = page.redirect.get(session)
+
+            if redirect:
+                return self.visit(redirect, url, depth + 1)
+
+            html = page.render(session)
+        except KeyboardInterrupt:
+            raise
+        except:
+            print "Page failure"
+
+            profile.print_stack_trace()
+
+            print "URL: %s" % url
+            print "Referer: %s" % referer
+
+            if self.continue_on_error:
+                html = ""
+                print_exc(file=sys.stderr)
+            else:
+                raise
+
+        return (html, profile)
+
+    def parse_html(self, html):
+        try:
+            parser = ParserCreate()
+            parser.Parse(html)
+        except KeyboardInterrupt:
+            raise
+        except:
+            print
+
+            html = html + "[eof]"
+
+            lines = html.split(os.linesep)
+
+            for i, line in enumerate(lines):
+                print "%4i %s" % (i + 1, line)
+
+            if self.continue_on_error:
+                print_exc(file=sys.stderr)
+            else:
+                raise
+
+    def run(self, max=-1):
+        urls = list()
+        visited = set()
+
+        count = 1
+        referer = None
+        url = ""
+
+        while url is not None:
+            visited.add(url)
+
+            print "%4i %-56s" % (count, truncate(url, 56)),
+
+            start = time()
+
+            html, profile = self.visit(url, referer, 0)
+
+            bytes = len(html)
+            millis = (time() - start) * 1000
+
+            print "[%5ib, %4ims]" % (bytes, millis)
+
+            if self.print_output:
+                print "-" * 80
+                print "  %s" % html.replace("\n", "\n  ")
+                print "-" * 80
+
+            #profile.compute_times()
+            #profile.print_results()
+            #profile.print_process_calls()
+            #profile.print_render_calls()
+
+            if count == max:
+                break
+
+            count += 1
+
+            for purl in profile.urls:
+                if purl not in visited:
+                    urls.append((purl, url))
+
+            if not urls:
+                break
+
+            url, referer = urls.pop()
+
+        self.print_profile()
+
+    def print_profile(self):
+        render_times_by_widget = defaultdict(list)
+
+        for profile in self.profiles:
+            profile.compute_times()
+            profile.collate_render_times(render_times_by_widget)
+
+        row = "%-120s  %8.3f  %8.3f  %8.3f  %8i  %8.3f"
+
+        print "-" * 80
+        #print row % ("Class", "Min", "Max", "Avg", "Count", "Total")
+
+        records = list()
+
+        for widget in render_times_by_widget:
+            times = render_times_by_widget[widget]
+
+            count = len(times)
+            total = sum(times)
+            avg = total / float(count)
+
+            records.append((widget, min(times), max(times), avg, count, total))
+
+        for record in sorted_by_index(records, 5):
+            print row % record
+
+def truncate(string, length):
+    if len(string) > length:
+        return string[:length]
+    else:
+        return string
+
+def sorted_by_index(seq, index):
+    return sorted(seq, cmp, lambda x: x[index])

Modified: mgmt/trunk/wooly/python/wooly/demo.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/demo.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/wooly/python/wooly/demo.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -31,8 +31,6 @@
 
         self.add_resource_dir(os.path.join(self.home, "resources"))
 
-        #self.enable_debug()
-
     def start(self):
         self.model.start()
 

Deleted: mgmt/trunk/wooly/python/wooly/devel.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/devel.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/wooly/python/wooly/devel.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -1,117 +0,0 @@
-import sys, os
-from random import sample
-from time import clock
-from xml.parsers.expat import ParserCreate
-from traceback import print_exc
-
-from wooly import *
-
-class BenchmarkHarness(object):
-    def __init__(self, app, check_xml=False):
-        self.app = app
-        self.check_xml = check_xml
-        self.continue_on_error = False
-
-    def run(self, max=-1):
-        visited = set()
-        self.app.debug.urls.add("")
-        count = 0
-
-        while True:
-            for url in self.app.debug.urls:
-                if url not in visited:
-                    visited.add(url)
-                    break
-
-            print count, url,
-
-            start = clock()
-
-            session = Session.unmarshal(self.app, url)
-
-            try:
-                page = session.page
-                page.process(session)
-
-                html = page.render(session)
-            except KeyboardInterrupt:
-                raise
-            except:
-                print
-
-                session.debug.print_last_call()
-
-                if self.continue_on_error:
-                    print_exc(file=sys.stderr)
-                else:
-                    raise
-
-            if self.check_xml:
-                try:
-                    parser = ParserCreate()
-                    parser.Parse(html)
-                except KeyboardInterrupt:
-                    raise
-                except:
-                    print
-
-                    html = html + "[eof]"
-
-                    lines = html.split(os.linesep)
-
-                    for i, line in enumerate(lines):
-                        print "%4i %s" % (i + 1, line)
-
-                    if self.continue_on_error:
-                        print_exc(file=sys.stderr)
-                    else:
-                        raise
-
-            bytes = len(html)
-            millis = (clock() - start) * 1000
-
-            print "[%i bytes, %i millis]" % (bytes, millis)
-
-            if count == max:
-                break
-
-            count += 1
-
-class DevelPage(Page):
-    html = """
-    <html>
-      <head>
-        <title>{title}</title>
-      </head>
-      <body>{rtrace}</body>
-    </html>
-    """
-
-    def __init__(self, app, name):
-        super(DevelPage, self).__init__(app, name)
-
-        self.render_trace = self.RenderTrace(app, "rtrace")
-        self.add_child(self.render_trace)
-
-    class RenderTrace(Widget):
-        def do_render(self, session):
-            writer = Writer()
-
-            writer.write("<ul>")
-
-            if self.app.debug and self.app.debug.sessions:
-                call = self.app.debug.sessions[-1].render_stack[0]
-                self.render_call(session, call, writer)
-
-            writer.write("</ul>")
-
-            return writer.to_string()
-
-        def render_call(self, session, call, writer):
-            writer.write("<li>%s</li>" % str(call.widget))
-            writer.write("<ul>")
-
-            for c in call.callees:
-                self.render_call(session, c, writer)
-
-            writer.write("</ul>")

Modified: mgmt/trunk/wooly/python/wooly/pages.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/pages.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/wooly/python/wooly/pages.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -137,12 +137,6 @@
     def render_base_name(self, session):
         return self.base_name
 
-    def render_problems(self, session):
-        if session.debug:
-            problems = session.debug.check()
-            if len(problems):
-                return "<!-- Widgets rendered but never processed: %s -->" % ", ".join([x.path for x in problems])
-
     class UpdatesAttribute(Attribute):
         def get_default(self, session):
             return list()
@@ -212,13 +206,6 @@
 
         return writer.to_string()
 
-    def render_problems(self, session):
-        sess = self.session.get(session)
-        if sess.debug:
-            problems = sess.debug.check()
-            if len(problems):
-                return "<!-- Widgets rendered but never processed: %s -->" % ", ".join([x.path for x in problems])
-
     def render_widget_id(self, session, widget):
         return widget.path
 
@@ -235,9 +222,7 @@
         def get(self, session):
             sess = super(UpdatePage.SessionParameter, self).get(session)
 
-            if hasattr(session, "user_session"):
-                sess.user_session = session.user_session
-
+            sess.client_session = session.client_session
             sess.background = True
 
             return sess

Modified: mgmt/trunk/wooly/python/wooly/pages.strings
===================================================================
--- mgmt/trunk/wooly/python/wooly/pages.strings	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/wooly/python/wooly/pages.strings	2009-11-03 17:10:20 UTC (rev 3690)
@@ -25,7 +25,6 @@
     {content}
   </body>
 </html>
-{problems}
 
 [AjaxScript.html]
 <script type="text/javascript">
@@ -42,7 +41,6 @@
   <!ENTITY nbsp "&#160;">
 ]>
 <widgets>{widgets}</widgets>
-{problems}
 
 [UpdatePage.widget_html]
 <widget id="{widget_id}">{widget}</widget>

Added: mgmt/trunk/wooly/python/wooly/profile.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/profile.py	                        (rev 0)
+++ mgmt/trunk/wooly/python/wooly/profile.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -0,0 +1,167 @@
+from time import time
+from collections import defaultdict
+
+class PageProfile(object):
+    def __init__(self, page):
+        self.page = page
+
+        self.process_calls = list()
+        self.render_calls = list()
+
+        self.current_calls = list()
+
+        self.urls = list()
+
+    def compute_times(self):
+        for calls in (self.process_calls, self.render_calls):
+            for call in calls:
+                call.compute_time()
+
+        for calls in (self.process_calls, self.render_calls):
+            for call in calls:
+                call.compute_self_time()
+
+    def collate_render_times(self, render_times_by_widget):
+        for call in self.render_calls:
+            render_times_by_widget[call.widget].append(call.self_time)
+
+    def print_results(self):
+        render_times_by_widget = defaultdict(list)
+
+        self.collate_render_times(render_times_by_widget)
+
+        row = "%-100s  %8.3f  %8.3f  %8.3f  %8i  %8.3f"
+
+        print "-" * 80
+        #print row % ("Class", "Min", "Max", "Avg", "Count", "Total")
+
+        for widget in sorted(render_times_by_widget):
+            times = render_times_by_widget[widget]
+
+            count = len(times)
+            total = sum(times)
+            avg = total / float(count)
+
+            print row % (widget, min(times), max(times), avg, count, total)
+
+    def print_stack_trace(self):
+        for call in self.current_calls:
+            print "in %s" % call
+
+    def print_process_calls(self):
+        if self.process_calls:
+            self.__print_calls(self.process_calls[-1])
+
+    def print_render_calls(self):
+        if self.render_calls:
+            self.__print_calls(self.render_calls[-1])
+
+    def __print_calls(self, root):
+        def visit(call, depth):
+            print "  " * depth,
+
+            if call.time is None:
+                time = "[incomplete]"
+            else:
+                time = "%.3f" % call.time
+
+            print "%s (%s)" % (call.widget, time)
+
+            for callee in call.callees:
+                visit(callee, depth + 1)
+
+        visit(root, 0)
+
+class WidgetCall(object):
+    def __init__(self, profile, widget):
+        self.profile = profile
+        self.widget = widget
+
+        self.profile
+
+        self.caller = None
+        self.callees = list()
+        
+        if self.profile.current_calls:
+            self.caller = self.profile.current_calls[-1]
+            self.caller.callees.append(self)
+
+        self.start = None
+        self.end = None
+
+        self.time = None
+        self.self_time = None
+
+    def write(self, writer):
+        writer.write(str(self.widget))
+        writer.write(" [%i]" % id(self))
+
+    def compute_time(self):
+        assert self.start is not None
+        assert self.end is not None
+        assert self.start < self.end
+
+        self.time = (self.end - self.start) * 1000
+
+    def compute_self_time(self):
+        assert self.time is not None
+
+        if self.callees:
+            callee_time = sum([x.time
+                               for x in self.callees
+                               if x.time is not None])
+
+            self.self_time = self.time - callee_time
+        else:
+            self.self_time = self.time
+
+    def __repr__(self):
+        return "%s(%s)" % (self.__class__.__name__, self.widget)
+
+class ProcessCall(WidgetCall):
+    def do(self, session):
+        self.profile.current_calls.append(self)
+
+        self.start = time()
+
+        if self.widget.update_enabled:
+            self.widget.page.enable_update(session, self.widget)
+
+        if self.widget.defer_enabled:
+            self.widget.page.enable_defer(session, self.widget)
+
+        args = self.widget.get_args(session)
+        self.widget.do_process(session, *args)
+
+        self.end = time()
+
+        self.profile.current_calls.pop()
+        self.profile.process_calls.append(self)
+
+class RenderCall(WidgetCall):
+    def do(self, session):
+        self.profile.current_calls.append(self)
+
+        self.start = time()
+
+        args = self.widget.get_args(session)
+        result = self.widget.do_render(session, *args)
+
+        if result is None:
+            result = ""
+
+        self.end = time()
+
+        assert self.start < self.end
+
+        if self.widget.parent:
+            cls = self.widget.__class__
+            name = self.widget.__repr__()
+            fmt = "<!-- \nopen: %s\n -->%s<!-- \nclose: %s\n -->"
+
+            result = fmt % (name, result, name)
+
+        self.profile.current_calls.pop()
+        self.profile.render_calls.append(self)
+
+        return result

Modified: mgmt/trunk/wooly/python/wooly/server.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/server.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/wooly/python/wooly/server.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -1,116 +1,124 @@
 from traceback import print_exc
-from datetime import datetime
+from datetime import datetime, timedelta
+from threading import Thread
 from time import strptime
+from uuid import uuid4
 from base64 import decodestring
+from parsley.threadingex import Lifecycle
+from wooly import *
 
-from wooly import *
-from devel import DevelPage
+from util import *
 from wsgiserver import CherryPyWSGIServer
-import logging
 
 log = logging.getLogger("wooly.server")
 
-class WebServer(object):
+class WebServer(Lifecycle):
     http_date = "%a, %d %b %Y %H:%M:%S %Z"
     http_date_gmt = "%a, %d %b %Y %H:%M:%S GMT"
 
     def __init__(self, app, addr, port):
+        self.log = log
+
         self.app = app
+
         self.addr = addr
         self.port = port
 
-        self.__server = CherryPyWSGIServer \
+        self.server = CherryPyWSGIServer \
             ((self.addr, self.port), self.service_request)
-        self.__server.environ["wsgi.version"] = (1, 1)
+        self.server.environ["wsgi.version"] = (1, 1)
 
+        self.client_sessions_by_id = dict()
+        self.client_session_expire_thread = ClientSessionExpireThread(self)
+
     def set_ssl_cert_path(self, path):
-        self.__server.ssl_certificate = path
+        self.server.ssl_certificate = path
 
     def set_ssl_key_path(self, path):
-        self.__server.ssl_private_key = path
+        self.server.ssl_private_key = path
 
-    def start(self):
-        self.__server.start()
+    def do_start(self):
+        self.server.start()
+        self.client_session_expire_thread.start()
 
-    def stop(self):
-        self.__server.stop()
+    def do_stop(self):
+        self.server.stop()
 
-    def authorized(self, session):
-        return False
+    def get_page(self, env):
+        name = env["PATH_INFO"][1:]
 
-    def service_request(self, env, response):
-        headers = list()
+        return self.app.pages_by_name.get(name)
 
-        name = env["PATH_INFO"][1:]
+    # XXX Now we're parsing cookies twice
+    def get_client_session(self, env, headers):
+        string = env.get("HTTP_COOKIE")
+        cookies_by_name = dict()
 
+        if string:
+            for crumb in string.split(";"):
+                name, value = crumb.split("=", 1)
+                cookies_by_name[name.strip()] = value.strip()
+
         try:
-            page = self.app.pages_by_name[name]
+            session_id = cookies_by_name["session"]
+            session = self.client_sessions_by_id[session_id]
         except KeyError:
-            return self.send_not_found(response, headers)
+            session = ClientSession()
+            self.client_sessions_by_id[session.id] = session
+            headers.append(("Set-Cookie", "session=%s" % session.id))
 
-        session = Session(page)
+        session.visited = datetime.now()
 
-        self.adapt_request_environment(env, session)
+        return session
 
-        if not self.authorized(session):
-            url = page.redirect.get(session)
+    def service_request(self, env, response):
+        headers = list()
 
-            if url:
-                return self.send_redirect(response, headers, session, url)
-            else:
-                return self.send_unauthorized(response, headers, session)
+        page = self.get_page(env)
 
-        try:
-            page.process(session)
-        except:
-            return self.send_error(response, headers, session)
+        if not page:
+            return self.send_not_found(response, headers)
 
-        url = page.redirect.get(session)
+        session = Session(page)
+        session.client_session = self.get_client_session(env, headers)
 
-        if url:
-            return self.send_redirect(response, headers, session, url)
-
         ims = env.get("HTTP_IF_MODIFIED_SINCE")
-        modified = page.get_last_modified(session).replace(microsecond=0)
 
         if ims:
+            modified = page.get_last_modified(session).replace(microsecond=0)
+
             try:
                 since = datetime(*strptime(str(ims), self.http_date)[0:6])
 
                 if modified <= since:
-                    return self.send_not_modified(response, headers, session)
+                    return self.send_not_modified(response, headers)
             except AttributeError:
                 log.error("Exception in If Modified Since. ims=%s" % str(ims))
                 pass
 
-        if modified:
-            when = modified.strftime(self.http_date_gmt)
-            headers.append(("Last-Modified", when))
+        self.adapt_request_to_session(env, session)
 
-        type = page.get_content_type(session)
+        try:
+            page.process(session)
 
-        if type:
-            headers.append(("Content-Type", type))
+            redirect = page.redirect.get(session)
 
-        cache = page.get_cache_control(session)
+            if redirect:
+                return self.send_redirect(response, headers, redirect)
 
-        if cache:
-            headers.append(("Cache-Control", cache))
-
-        try:
             content = page.render(session)
         except:
-            return self.send_error(response, headers, session)
+            return self.send_error(response, headers)
 
         headers.append(("Content-Length", str(len(content))))
 
-        self.send_response(response, headers, session, "200 OK")
+        self.adapt_session_to_response(page, session, headers)
 
-        page.save_session(session)
+        response("200 OK", headers)
 
         return (content,)
 
-    def adapt_request_environment(self, env, session):
+    def adapt_request_to_session(self, env, session):
         session.unmarshal_url_vars(env["QUERY_STRING"])
 
         if env["REQUEST_METHOD"] == "POST":
@@ -136,6 +144,23 @@
                 name = key[5:].lower()
                 session.headers_by_name[name] = value
 
+    def adapt_session_to_response(self, page, session, headers):
+        last_modified = page.get_last_modified(session)
+
+        if last_modified:
+            when = last_modified.strftime(self.http_date_gmt)
+            headers.append(("Last-Modified", when))
+
+        content_type = page.get_content_type(session)
+
+        if content_type:
+            headers.append(("Content-Type", content_type))
+
+        cache = page.get_cache_control(session)
+
+        if cache:
+            headers.append(("Cache-Control", cache))
+
     def send_not_found(self, response, headers):
         message = "404 Not Found"
 
@@ -145,52 +170,61 @@
         response(message, headers)
         return (message,)
 
-    def send_response(self, response, headers, session, message):
-        for header in session.marshal_cookies():
-            headers.append(("Set-Cookie", header))
-
-        response(message, headers)
-
-    def send_message(self, response, headers, session, message):
+    def send_message(self, response, headers, message):
         headers.append(("Content-Length", str(len(message))))
         headers.append(("Content-Type", "text/plain"))
 
-        self.send_response(response, headers, session, message)
+        response(message, headers)
 
         return (message,)
 
-    def send_unauthorized(self, response, headers, session):
-        return self.send_message \
-            (response, headers, session, "401 Unauthorized")
-
-    def send_redirect(self, response, headers, session, url):
+    def send_redirect(self, response, headers, url):
         headers.append(("Location", url))
         headers.append(("Content-Length", "0"))
 
-        self.send_response(response, headers, session, "303 See Other")
+        response("303 See Other", headers)
 
         return ()
 
-    def send_not_modified(self, response, headers, session):
+    def send_not_modified(self, response, headers):
         headers.append(("Content-Length", "0"))
 
-        self.send_response(response, headers, session, "304 Not Modified")
+        response("304 Not Modified", headers)
 
         return ()
 
-    def send_error(self, response, headers, session):
+    def send_error(self, response, headers):
         headers.append(("Content-Type", "text/plain"))
 
-        self.send_response(response, headers, session, "500 Internal Error")
+        response("500 Internal Error", headers)
 
         writer = Writer()
-        writer.write("APPLICATION ERROR\n")
+        writer.write("APPLICATION ERROR\n\n")
 
-        if session.debug:
-            writer.write("\n----- wooly -----\n\n")
-            session.debug.write(writer)
-
-        writer.write("\n----- python -----\n\n")
         print_exc(None, writer)
 
         return writer.to_string()
+
+class ClientSessionExpireThread(Thread):
+    def __init__(self, server):
+        super(ClientSessionExpireThread, self).__init__()
+
+        self.server = server
+
+        self.setDaemon(True)
+
+    def run(self):
+        while True:
+            self.expire_sessions()
+            sleep(60)
+
+    def expire_sessions(self):
+        when = datetime.now() - timedelta(hours=1)
+        count = 0
+
+        for session in self.client_sessions_by_id.values():
+            if session.visited < when:
+                del self.server.client_sessions_by_id[session.id]
+                count += 1
+
+        log.info("Expired %i client sessions", count)

Modified: mgmt/trunk/wooly/python/wooly/sql.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/sql.py	2009-11-03 12:47:51 UTC (rev 3689)
+++ mgmt/trunk/wooly/python/wooly/sql.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -117,6 +117,7 @@
 
     def render_sql_where(self, session):
         exprs = self.where_exprs.get(session)
+
         return "where %s" % " and ".join(exprs)
 
     def render_sql_order_by(self, session):

Added: mgmt/trunk/wooly/python/wooly/util.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/util.py	                        (rev 0)
+++ mgmt/trunk/wooly/python/wooly/util.py	2009-11-03 17:10:20 UTC (rev 3690)
@@ -0,0 +1,4 @@
+import logging
+
+from datetime import datetime
+from time import clock



More information about the rhmessaging-commits mailing list