Filewatcher File Search
FTP Search
  
Directory (beta)
  
Content Search (beta)
   
pkg://TurboGears-1.0.4.2-3.fc7.src.rpm:1112792/TurboGears-1.0.4.2.tar.gz  info  downloads

TurboGears-1.0.4.2/0000755000175000017500000000000010745210077012636 5ustar  faidefaideTurboGears-1.0.4.2/LICENSE.txt0000644000175000017500000000226010710636656014470 0ustar  faidefaideThis is the MIT license:
http://www.opensource.org/licenses/mit-license.php

Copyright (c) 2005-2007 Kevin Dangoor and contributors. TurboGears is a trademark of Kevin Dangoor.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.TurboGears-1.0.4.2/TurboGears.egg-info/0000755000175000017500000000000010745210077016405 5ustar  faidefaideTurboGears-1.0.4.2/TurboGears.egg-info/SOURCES.txt0000644000175000017500000006037610745210076020304 0ustar  faidefaideCHANGELOG.txt
CONTRIBUTORS.txt
LICENSE.txt
README.txt
codename.py
ez_setup.py
setup.cfg
setup.py
toolbox-start.py
TurboGears.egg-info/PKG-INFO
TurboGears.egg-info/SOURCES.txt
TurboGears.egg-info/dependency_links.txt
TurboGears.egg-info/entry_points.txt
TurboGears.egg-info/not-zip-safe
TurboGears.egg-info/requires.txt
TurboGears.egg-info/top_level.txt
apigen/setup.py
apigen/apigen/__init__.py
apigen/apigen/colorizer.py
apigen/apigen/command.py
apigen/apigen/element_colorizer.py
apigen/apigen/pythondoc.py
apigen/apigen/release.py
apigen/apigen.egg-info/PKG-INFO
apigen/apigen.egg-info/SOURCES.txt
apigen/apigen.egg-info/entry_points.txt
apigen/apigen.egg-info/not-zip-safe
apigen/apigen.egg-info/top_level.txt
thirdparty/externals.py
turbogears/__init__.py
turbogears/config.py
turbogears/controllers.py
turbogears/database.py
turbogears/decorator.py
turbogears/docgen.py
turbogears/errorhandling.py
turbogears/finddata.py
turbogears/genericfunctions.py
turbogears/paginate.py
turbogears/release.py
turbogears/scheduler.py
turbogears/startup.py
turbogears/testutil.py
turbogears/util.py
turbogears/validators.py
turbogears/command/__init__.py
turbogears/command/base.py
turbogears/command/i18n.py
turbogears/command/info.py
turbogears/command/quickstart.py
turbogears/command/sacommand.py
turbogears/feed/__init__.py
turbogears/feed/atom0_3.kid
turbogears/feed/atom1_0.kid
turbogears/feed/feed.py
turbogears/feed/rss2_0.kid
turbogears/i18n/__init__.py
turbogears/i18n/format.py
turbogears/i18n/kidutils.py
turbogears/i18n/tg_gettext.py
turbogears/i18n/utils.py
turbogears/i18n/data/TurboGears.pot
turbogears/i18n/data/__init__.py
turbogears/i18n/data/af.py
turbogears/i18n/data/af_ZA.py
turbogears/i18n/data/am.py
turbogears/i18n/data/am_ET.py
turbogears/i18n/data/ar.py
turbogears/i18n/data/ar_AE.py
turbogears/i18n/data/ar_BH.py
turbogears/i18n/data/ar_DZ.py
turbogears/i18n/data/ar_EG.py
turbogears/i18n/data/ar_IN.py
turbogears/i18n/data/ar_IQ.py
turbogears/i18n/data/ar_JO.py
turbogears/i18n/data/ar_KW.py
turbogears/i18n/data/ar_LB.py
turbogears/i18n/data/ar_LY.py
turbogears/i18n/data/ar_MA.py
turbogears/i18n/data/ar_OM.py
turbogears/i18n/data/ar_QA.py
turbogears/i18n/data/ar_SA.py
turbogears/i18n/data/ar_SD.py
turbogears/i18n/data/ar_SY.py
turbogears/i18n/data/ar_TN.py
turbogears/i18n/data/ar_YE.py
turbogears/i18n/data/be.py
turbogears/i18n/data/be_BY.py
turbogears/i18n/data/bg.py
turbogears/i18n/data/bg_BG.py
turbogears/i18n/data/bn.py
turbogears/i18n/data/bn_IN.py
turbogears/i18n/data/ca.py
turbogears/i18n/data/ca_ES.py
turbogears/i18n/data/cs.py
turbogears/i18n/data/cs_CZ.py
turbogears/i18n/data/da.py
turbogears/i18n/data/da_DK.py
turbogears/i18n/data/de.py
turbogears/i18n/data/de_AT.py
turbogears/i18n/data/de_BE.py
turbogears/i18n/data/de_CH.py
turbogears/i18n/data/de_DE.py
turbogears/i18n/data/de_LU.py
turbogears/i18n/data/el.py
turbogears/i18n/data/el_GR.py
turbogears/i18n/data/en.py
turbogears/i18n/data/en_AU.py
turbogears/i18n/data/en_BE.py
turbogears/i18n/data/en_BW.py
turbogears/i18n/data/en_CA.py
turbogears/i18n/data/en_GB.py
turbogears/i18n/data/en_HK.py
turbogears/i18n/data/en_IE.py
turbogears/i18n/data/en_IN.py
turbogears/i18n/data/en_MT.py
turbogears/i18n/data/en_NZ.py
turbogears/i18n/data/en_PH.py
turbogears/i18n/data/en_SG.py
turbogears/i18n/data/en_US.py
turbogears/i18n/data/en_US_POSIX.py
turbogears/i18n/data/en_VI.py
turbogears/i18n/data/en_ZA.py
turbogears/i18n/data/en_ZW.py
turbogears/i18n/data/eo.py
turbogears/i18n/data/es.py
turbogears/i18n/data/es_AR.py
turbogears/i18n/data/es_BO.py
turbogears/i18n/data/es_CL.py
turbogears/i18n/data/es_CO.py
turbogears/i18n/data/es_CR.py
turbogears/i18n/data/es_DO.py
turbogears/i18n/data/es_EC.py
turbogears/i18n/data/es_ES.py
turbogears/i18n/data/es_GT.py
turbogears/i18n/data/es_HN.py
turbogears/i18n/data/es_MX.py
turbogears/i18n/data/es_NI.py
turbogears/i18n/data/es_PA.py
turbogears/i18n/data/es_PE.py
turbogears/i18n/data/es_PR.py
turbogears/i18n/data/es_PY.py
turbogears/i18n/data/es_SV.py
turbogears/i18n/data/es_US.py
turbogears/i18n/data/es_UY.py
turbogears/i18n/data/es_VE.py
turbogears/i18n/data/et.py
turbogears/i18n/data/et_EE.py
turbogears/i18n/data/eu.py
turbogears/i18n/data/eu_ES.py
turbogears/i18n/data/fa.py
turbogears/i18n/data/fa_AF.py
turbogears/i18n/data/fa_IR.py
turbogears/i18n/data/fi.py
turbogears/i18n/data/fi_FI.py
turbogears/i18n/data/fo.py
turbogears/i18n/data/fo_FO.py
turbogears/i18n/data/fr.py
turbogears/i18n/data/fr_BE.py
turbogears/i18n/data/fr_CA.py
turbogears/i18n/data/fr_CH.py
turbogears/i18n/data/fr_FR.py
turbogears/i18n/data/fr_LU.py
turbogears/i18n/data/ga.py
turbogears/i18n/data/ga_IE.py
turbogears/i18n/data/gl.py
turbogears/i18n/data/gl_ES.py
turbogears/i18n/data/gu.py
turbogears/i18n/data/gu_IN.py
turbogears/i18n/data/gv.py
turbogears/i18n/data/gv_GB.py
turbogears/i18n/data/he.py
turbogears/i18n/data/he_IL.py
turbogears/i18n/data/hi.py
turbogears/i18n/data/hi_IN.py
turbogears/i18n/data/hr.py
turbogears/i18n/data/hr_HR.py
turbogears/i18n/data/hu.py
turbogears/i18n/data/hu_HU.py
turbogears/i18n/data/hy.py
turbogears/i18n/data/hy_AM.py
turbogears/i18n/data/hy_AM_REVISED.py
turbogears/i18n/data/id.py
turbogears/i18n/data/id_ID.py
turbogears/i18n/data/is.py
turbogears/i18n/data/is_IS.py
turbogears/i18n/data/it.py
turbogears/i18n/data/it_CH.py
turbogears/i18n/data/it_IT.py
turbogears/i18n/data/ja.py
turbogears/i18n/data/ja_JP.py
turbogears/i18n/data/kk.py
turbogears/i18n/data/kk_KZ.py
turbogears/i18n/data/kl.py
turbogears/i18n/data/kl_GL.py
turbogears/i18n/data/kn.py
turbogears/i18n/data/kn_IN.py
turbogears/i18n/data/ko.py
turbogears/i18n/data/ko_KR.py
turbogears/i18n/data/kok.py
turbogears/i18n/data/kok_IN.py
turbogears/i18n/data/kw.py
turbogears/i18n/data/kw_GB.py
turbogears/i18n/data/lt.py
turbogears/i18n/data/lt_LT.py
turbogears/i18n/data/lv.py
turbogears/i18n/data/lv_LV.py
turbogears/i18n/data/mk.py
turbogears/i18n/data/mk_MK.py
turbogears/i18n/data/mr.py
turbogears/i18n/data/mr_IN.py
turbogears/i18n/data/ms.py
turbogears/i18n/data/ms_BN.py
turbogears/i18n/data/ms_MY.py
turbogears/i18n/data/mt.py
turbogears/i18n/data/mt_MT.py
turbogears/i18n/data/nb.py
turbogears/i18n/data/nb_NO.py
turbogears/i18n/data/nl.py
turbogears/i18n/data/nl_BE.py
turbogears/i18n/data/nl_NL.py
turbogears/i18n/data/nn.py
turbogears/i18n/data/nn_NO.py
turbogears/i18n/data/no.py
turbogears/i18n/data/no_NO.py
turbogears/i18n/data/om.py
turbogears/i18n/data/om_ET.py
turbogears/i18n/data/om_KE.py
turbogears/i18n/data/pa.py
turbogears/i18n/data/pa_IN.py
turbogears/i18n/data/pl.py
turbogears/i18n/data/pl_PL.py
turbogears/i18n/data/ps.py
turbogears/i18n/data/ps_AF.py
turbogears/i18n/data/pt.py
turbogears/i18n/data/pt_BR.py
turbogears/i18n/data/pt_PT.py
turbogears/i18n/data/ro.py
turbogears/i18n/data/ro_RO.py
turbogears/i18n/data/ru.py
turbogears/i18n/data/ru_RU.py
turbogears/i18n/data/ru_UA.py
turbogears/i18n/data/sh.py
turbogears/i18n/data/sh_YU.py
turbogears/i18n/data/sk.py
turbogears/i18n/data/sk_SK.py
turbogears/i18n/data/sl.py
turbogears/i18n/data/sl_SI.py
turbogears/i18n/data/so.py
turbogears/i18n/data/so_DJ.py
turbogears/i18n/data/so_ET.py
turbogears/i18n/data/so_KE.py
turbogears/i18n/data/so_SO.py
turbogears/i18n/data/sq.py
turbogears/i18n/data/sq_AL.py
turbogears/i18n/data/sr.py
turbogears/i18n/data/sr_YU.py
turbogears/i18n/data/sv.py
turbogears/i18n/data/sv_FI.py
turbogears/i18n/data/sv_SE.py
turbogears/i18n/data/sw.py
turbogears/i18n/data/sw_KE.py
turbogears/i18n/data/sw_TZ.py
turbogears/i18n/data/ta.py
turbogears/i18n/data/ta_IN.py
turbogears/i18n/data/te.py
turbogears/i18n/data/te_IN.py
turbogears/i18n/data/th.py
turbogears/i18n/data/th_TH.py
turbogears/i18n/data/ti.py
turbogears/i18n/data/ti_ER.py
turbogears/i18n/data/ti_ET.py
turbogears/i18n/data/tr.py
turbogears/i18n/data/tr_TR.py
turbogears/i18n/data/uk.py
turbogears/i18n/data/uk_UA.py
turbogears/i18n/data/vi.py
turbogears/i18n/data/vi_VN.py
turbogears/i18n/data/zh.py
turbogears/i18n/data/zh_CN.py
turbogears/i18n/data/zh_HK.py
turbogears/i18n/data/zh_MO.py
turbogears/i18n/data/zh_SG.py
turbogears/i18n/data/zh_TW.py
turbogears/i18n/data/de/LC_MESSAGES/TurboGears.mo
turbogears/i18n/data/de/LC_MESSAGES/TurboGears.po
turbogears/i18n/data/sl/LC_MESSAGES/TurboGears.mo
turbogears/i18n/data/sl/LC_MESSAGES/TurboGears.po
turbogears/i18n/sogettext/__init__.py
turbogears/i18n/sogettext/model.py
turbogears/i18n/tests/__init__.py
turbogears/i18n/tests/test_format.py
turbogears/i18n/tests/test_kidutils.py
turbogears/i18n/tests/test_so_gettext.py
turbogears/i18n/tests/test_tg_gettext.py
turbogears/i18n/tests/test_utils.py
turbogears/identity/__init__.py
turbogears/identity/conditions.py
turbogears/identity/exceptions.py
turbogears/identity/saprovider.py
turbogears/identity/soprovider.py
turbogears/identity/visitor.py
turbogears/identity/tests/__init__.py
turbogears/identity/tests/test_identity.py
turbogears/identity/tests/test_visit.py
turbogears/qstemplates/__init__.py
turbogears/qstemplates/qsbase/+einame+.egg-info/PKG-INFO
turbogears/qstemplates/qsbase/+einame+.egg-info/paster_plugins.txt
turbogears/qstemplates/qsbase/+einame+.egg-info/sqlobject.txt_tmpl
turbogears/qstemplates/qsbase/+package+/__init__.py
turbogears/qstemplates/qsbase/+package+/release.py_tmpl
turbogears/qstemplates/qsbase/+package+/static/css/empty_tmpl
turbogears/qstemplates/qsbase/+package+/static/images/favicon.ico
turbogears/qstemplates/qsbase/+package+/static/images/tg_under_the_hood.png
turbogears/qstemplates/qsbase/+package+/static/images/under_the_hood_blue.png
turbogears/qstemplates/qsbase/+package+/static/javascript/empty_tmpl
turbogears/qstemplates/qsbase/+package+/templates/__init__.py
turbogears/qstemplates/quickstart/README.txt_tmpl
turbogears/qstemplates/quickstart/dev.cfg_tmpl
turbogears/qstemplates/quickstart/sample-prod.cfg_tmpl
turbogears/qstemplates/quickstart/setup.py_tmpl
turbogears/qstemplates/quickstart/start-+package+.py_tmpl
turbogears/qstemplates/quickstart/test.cfg_tmpl
turbogears/qstemplates/quickstart/+package+/commands.py_tmpl
turbogears/qstemplates/quickstart/+package+/controllers.py_tmpl
turbogears/qstemplates/quickstart/+package+/json.py_tmpl
turbogears/qstemplates/quickstart/+package+/model.py_tmpl
turbogears/qstemplates/quickstart/+package+/config/__init__.py
turbogears/qstemplates/quickstart/+package+/config/app.cfg_tmpl
turbogears/qstemplates/quickstart/+package+/config/log.cfg_tmpl
turbogears/qstemplates/quickstart/+package+/static/css/style.css
turbogears/qstemplates/quickstart/+package+/static/images/header_inner.png
turbogears/qstemplates/quickstart/+package+/static/images/info.png
turbogears/qstemplates/quickstart/+package+/static/images/ok.png
turbogears/qstemplates/quickstart/+package+/templates/login.kid
turbogears/qstemplates/quickstart/+package+/templates/master.kid
turbogears/qstemplates/quickstart/+package+/templates/welcome.kid
turbogears/qstemplates/quickstart/+package+/tests/__init__.py
turbogears/qstemplates/quickstart/+package+/tests/test_controllers.py_tmpl
turbogears/qstemplates/quickstart/+package+/tests/test_model.py_tmpl
turbogears/qstemplates/quickstartbig/+package+/controllers/__init__.py_tmpl
turbogears/qstemplates/quickstartbig/+package+/controllers/root.py_tmpl
turbogears/qstemplates/widget/README.txt_tmpl
turbogears/qstemplates/widget/setup.py_tmpl
turbogears/qstemplates/widget/+package+/widgets.py_tmpl
turbogears/static/__init__.py
turbogears/static/css/__init__.py
turbogears/static/css/interpreter.css
turbogears/static/css/toolbox.css
turbogears/static/css/widget.css
turbogears/static/images/__init__.py
turbogears/static/images/add.png
turbogears/static/images/admi18n.png
turbogears/static/images/arrow_down.png
turbogears/static/images/arrow_down_small.png
turbogears/static/images/arrow_left.png
turbogears/static/images/arrow_right.png
turbogears/static/images/arrow_up.png
turbogears/static/images/arrow_up_small.png
turbogears/static/images/catwalk.png
turbogears/static/images/catwalk_logo.png
turbogears/static/images/catwalk_logo_bg.png
turbogears/static/images/column_chooser.png
turbogears/static/images/designer.png
turbogears/static/images/discard.png
turbogears/static/images/edit.png
turbogears/static/images/file.png
turbogears/static/images/folder.png
turbogears/static/images/identity.png
turbogears/static/images/info.png
turbogears/static/images/play.png
turbogears/static/images/remove.png
turbogears/static/images/save.png
turbogears/static/images/shell.png
turbogears/static/images/small_gear.png
turbogears/static/images/stop.png
turbogears/static/images/table.gif
turbogears/static/images/tg_power.png
turbogears/static/images/tg_under_the_hood.png
turbogears/static/images/tg_under_the_hood_sans_gear.png
turbogears/static/images/toolbox_logo.png
turbogears/static/images/toolbox_top_baggrund.png
turbogears/static/images/toolbox_top_vertical_line.png
turbogears/static/images/top.png
turbogears/static/images/top_bg.png
turbogears/static/images/transp.png
turbogears/static/images/trash.png
turbogears/static/images/widgets.png
turbogears/static/js/MochiKit.js
turbogears/static/js/__init__.py
turbogears/static/js/i18n_base.js
turbogears/static/js/interpreter.js
turbogears/static/js/widget.js
turbogears/static/js/tool-man/cookies.js
turbogears/static/js/tool-man/coordinates.js
turbogears/static/js/tool-man/core.js
turbogears/static/js/tool-man/css.js
turbogears/static/js/tool-man/drag.js
turbogears/static/js/tool-man/dragsort.js
turbogears/static/js/tool-man/events.js
turbogears/tests/__init__.py
turbogears/tests/config.cfg
turbogears/tests/configfile.cfg
turbogears/tests/form.kid
turbogears/tests/othertemplate.kid
turbogears/tests/paginate.kid
turbogears/tests/simple.kid
turbogears/tests/simplecheetah.tmpl
turbogears/tests/test_catwalk.py
turbogears/tests/test_command_i18n.py
turbogears/tests/test_config.py
turbogears/tests/test_controllers.py
turbogears/tests/test_database.py
turbogears/tests/test_decorator.py
turbogears/tests/test_errorhandling.py
turbogears/tests/test_expose.py
turbogears/tests/test_form_controllers.py
turbogears/tests/test_genericfunctions.py
turbogears/tests/test_paginate.py
turbogears/tests/test_sqlalchemy.py
turbogears/tests/test_testutil.py
turbogears/tests/test_validators.py
turbogears/tests/test_view.py
turbogears/tests/textfmt.tmpl
turbogears/tests/util.py
turbogears/tests/catwalk_models/__init__.py
turbogears/tests/catwalk_models/browse.py
turbogears/tests/catwalk_models/model_list.py
turbogears/tests/catwalk_models/model_structure.py
turbogears/tests/catwalk_models/single_join.py
turbogears/tests/catwalk_models/catwalk-session/session.pkl
turbogears/tests/locale/en/LC_MESSAGES/messages.mo
turbogears/tests/locale/en/LC_MESSAGES/messages.po
turbogears/tests/locale/fi/LC_MESSAGES/messages.mo
turbogears/tests/locale/fi/LC_MESSAGES/messages.po
turbogears/toolbox/__init__.py
turbogears/toolbox/base.py
turbogears/toolbox/console.kid
turbogears/toolbox/info.kid
turbogears/toolbox/main.kid
turbogears/toolbox/master.kid
turbogears/toolbox/shell.py
turbogears/toolbox/widgets.kid
turbogears/toolbox/admi18n/__init__.py
turbogears/toolbox/admi18n/catalog.py
turbogears/toolbox/admi18n/inter.kid
turbogears/toolbox/admi18n/internationalization.kid
turbogears/toolbox/admi18n/language.kid
turbogears/toolbox/admi18n/languageManagement.kid
turbogears/toolbox/admi18n/msgfmt.py
turbogears/toolbox/admi18n/po_view.kid
turbogears/toolbox/admi18n/pygettext.py
turbogears/toolbox/admi18n/stringCollection.kid
turbogears/toolbox/catwalk/__init__.py
turbogears/toolbox/catwalk/browse.py
turbogears/toolbox/catwalk/browse_grid.kid
turbogears/toolbox/catwalk/catwalk.kid
turbogears/toolbox/catwalk/columns.kid
turbogears/toolbox/catwalk/static/css/catwalk.css
turbogears/toolbox/catwalk/static/javascript/browse.js
turbogears/toolbox/catwalk/static/javascript/catwalk.js
turbogears/toolbox/catwalk/static/javascript/greybox/AmiJS.js
turbogears/toolbox/catwalk/static/javascript/greybox/blank.gif
turbogears/toolbox/catwalk/static/javascript/greybox/close.gif
turbogears/toolbox/catwalk/static/javascript/greybox/greybox.css
turbogears/toolbox/catwalk/static/javascript/greybox/greybox.js
turbogears/toolbox/catwalk/static/javascript/greybox/greybox__.js
turbogears/toolbox/catwalk/static/javascript/greybox/greybox_inline.js
turbogears/toolbox/catwalk/static/javascript/greybox/logo.png
turbogears/toolbox/catwalk/static/javascript/greybox/overlay.png
turbogears/toolbox/designer/__init__.py
turbogears/toolbox/designer/modelDesigner.kid
turbogears/toolbox/designer/static/css/style.css
turbogears/toolbox/designer/static/diagram/index.html
turbogears/toolbox/designer/static/diagram/images/back.gif
turbogears/toolbox/designer/static/diagram/images/shadow.png
turbogears/toolbox/designer/static/diagram/images/shadow_bottom.png
turbogears/toolbox/designer/static/diagram/images/shadow_corner.png
turbogears/toolbox/designer/static/diagram/images/shadow_right.png
turbogears/toolbox/designer/static/diagram/js/ajax.js
turbogears/toolbox/designer/static/diagram/js/animator.js
turbogears/toolbox/designer/static/diagram/js/generic.js
turbogears/toolbox/designer/static/diagram/js/io.js
turbogears/toolbox/designer/static/diagram/js/main.js
turbogears/toolbox/designer/static/diagram/js/objects.js
turbogears/toolbox/designer/static/diagram/js/settings.js
turbogears/toolbox/designer/static/diagram/js/sql_types.js
turbogears/toolbox/designer/static/diagram/styles/bar.css
turbogears/toolbox/designer/static/diagram/styles/foo
turbogears/toolbox/designer/static/diagram/styles/style.css
turbogears/toolbox/designer/static/diagram/styles/style.js
turbogears/toolbox/designer/static/images/info.png
turbogears/toolbox/designer/static/javascript/modelDesigner.js
turbogears/toolbox/designer/static/sessions/Survey.js
turbogears/toolbox/designer/static/sessions/TurboTunes.js
turbogears/toolbox/designer/static/sessions/WebShop.js
turbogears/view/__init__.py
turbogears/view/base.py
turbogears/view/templates/__init__.py
turbogears/view/templates/sitetemplate.kid
turbogears/visit/__init__.py
turbogears/visit/api.py
turbogears/visit/savisit.py
turbogears/visit/sovisit.py
turbogears/widgets/__init__.py
turbogears/widgets/base.py
turbogears/widgets/big_widgets.py
turbogears/widgets/datagrid.py
turbogears/widgets/forms.py
turbogears/widgets/i18n.py
turbogears/widgets/links.py
turbogears/widgets/meta.py
turbogears/widgets/rpc.py
turbogears/widgets/static/ajax.js
turbogears/widgets/static/ajaxgrid.js
turbogears/widgets/static/autocompletefield.css
turbogears/widgets/static/autocompletefield.js
turbogears/widgets/static/grid.css
turbogears/widgets/static/img.gif
turbogears/widgets/static/spinner.gif
turbogears/widgets/static/spinnerstopped.png
turbogears/widgets/static/calendar/calendar-blue.css
turbogears/widgets/static/calendar/calendar-blue2.css
turbogears/widgets/static/calendar/calendar-brown.css
turbogears/widgets/static/calendar/calendar-green.css
turbogears/widgets/static/calendar/calendar-setup.js
turbogears/widgets/static/calendar/calendar-system.css
turbogears/widgets/static/calendar/calendar-tas.css
turbogears/widgets/static/calendar/calendar-win2k-1.css
turbogears/widgets/static/calendar/calendar-win2k-2.css
turbogears/widgets/static/calendar/calendar-win2k-cold-1.css
turbogears/widgets/static/calendar/calendar-win2k-cold-2.css
turbogears/widgets/static/calendar/calendar.js
turbogears/widgets/static/calendar/menuarrow.gif
turbogears/widgets/static/calendar/menuarrow2.gif
turbogears/widgets/static/calendar/lang/calendar-af.js
turbogears/widgets/static/calendar/lang/calendar-al.js
turbogears/widgets/static/calendar/lang/calendar-bg.js
turbogears/widgets/static/calendar/lang/calendar-big5-utf-8.js
turbogears/widgets/static/calendar/lang/calendar-big5-utf8.js
turbogears/widgets/static/calendar/lang/calendar-big5.js
turbogears/widgets/static/calendar/lang/calendar-br.js
turbogears/widgets/static/calendar/lang/calendar-ca.js
turbogears/widgets/static/calendar/lang/calendar-cs-utf-8.js
turbogears/widgets/static/calendar/lang/calendar-cs-utf8.js
turbogears/widgets/static/calendar/lang/calendar-cs-win.js
turbogears/widgets/static/calendar/lang/calendar-da.js
turbogears/widgets/static/calendar/lang/calendar-de.js
turbogears/widgets/static/calendar/lang/calendar-du.js
turbogears/widgets/static/calendar/lang/calendar-el.js
turbogears/widgets/static/calendar/lang/calendar-en.js
turbogears/widgets/static/calendar/lang/calendar-es.js
turbogears/widgets/static/calendar/lang/calendar-fi.js
turbogears/widgets/static/calendar/lang/calendar-fr.js
turbogears/widgets/static/calendar/lang/calendar-he-utf-8.js
turbogears/widgets/static/calendar/lang/calendar-he-utf8.js
turbogears/widgets/static/calendar/lang/calendar-hr-utf-8.js
turbogears/widgets/static/calendar/lang/calendar-hr-utf8.js
turbogears/widgets/static/calendar/lang/calendar-hr.js
turbogears/widgets/static/calendar/lang/calendar-hu.js
turbogears/widgets/static/calendar/lang/calendar-it.js
turbogears/widgets/static/calendar/lang/calendar-jp.js
turbogears/widgets/static/calendar/lang/calendar-ko-utf-8.js
turbogears/widgets/static/calendar/lang/calendar-ko-utf8.js
turbogears/widgets/static/calendar/lang/calendar-ko.js
turbogears/widgets/static/calendar/lang/calendar-lt-utf-8.js
turbogears/widgets/static/calendar/lang/calendar-lt-utf8.js
turbogears/widgets/static/calendar/lang/calendar-lt.js
turbogears/widgets/static/calendar/lang/calendar-lv.js
turbogears/widgets/static/calendar/lang/calendar-nl.js
turbogears/widgets/static/calendar/lang/calendar-no.js
turbogears/widgets/static/calendar/lang/calendar-pl-utf-8.js
turbogears/widgets/static/calendar/lang/calendar-pl-utf8.js
turbogears/widgets/static/calendar/lang/calendar-pl.js
turbogears/widgets/static/calendar/lang/calendar-pt-utf-8.js
turbogears/widgets/static/calendar/lang/calendar-pt-utf8.js
turbogears/widgets/static/calendar/lang/calendar-pt.js
turbogears/widgets/static/calendar/lang/calendar-ro.js
turbogears/widgets/static/calendar/lang/calendar-ru.js
turbogears/widgets/static/calendar/lang/calendar-ru_win_.js
turbogears/widgets/static/calendar/lang/calendar-si.js
turbogears/widgets/static/calendar/lang/calendar-sk.js
turbogears/widgets/static/calendar/lang/calendar-sp.js
turbogears/widgets/static/calendar/lang/calendar-sv.js
turbogears/widgets/static/calendar/lang/calendar-tr.js
turbogears/widgets/static/calendar/lang/calendar-zh-cn-utf-8.js
turbogears/widgets/static/calendar/lang/calendar-zh-cn-utf8.js
turbogears/widgets/static/calendar/lang/calendar-zh-cn.js
turbogears/widgets/static/calendar/lang/calendar-zh-tw-utf-8.js
turbogears/widgets/static/calendar/lang/calendar-zh-tw-utf8.js
turbogears/widgets/static/calendar/lang/calendar-zh-tw.js
turbogears/widgets/static/calendar/lang/calendar-zh-utf-8.js
turbogears/widgets/static/calendar/lang/calendar-zh-utf8.js
turbogears/widgets/static/calendar/lang/calendar-zh.js
turbogears/widgets/static/calendar/skins/aqua/active-bg.gif
turbogears/widgets/static/calendar/skins/aqua/dark-bg.gif
turbogears/widgets/static/calendar/skins/aqua/hover-bg.gif
turbogears/widgets/static/calendar/skins/aqua/menuarrow.gif
turbogears/widgets/static/calendar/skins/aqua/normal-bg.gif
turbogears/widgets/static/calendar/skins/aqua/rowhover-bg.gif
turbogears/widgets/static/calendar/skins/aqua/status-bg.gif
turbogears/widgets/static/calendar/skins/aqua/theme.css
turbogears/widgets/static/calendar/skins/aqua/title-bg.gif
turbogears/widgets/static/calendar/skins/aqua/today-bg.gif
turbogears/widgets/static/sh/SyntaxHighlighter.css
turbogears/widgets/static/sh/shBrushCSharp.js
turbogears/widgets/static/sh/shBrushCss.js
turbogears/widgets/static/sh/shBrushDelphi.js
turbogears/widgets/static/sh/shBrushJScript.js
turbogears/widgets/static/sh/shBrushJava.js
turbogears/widgets/static/sh/shBrushPhp.js
turbogears/widgets/static/sh/shBrushPython.js
turbogears/widgets/static/sh/shBrushRuby.js
turbogears/widgets/static/sh/shBrushSql.js
turbogears/widgets/static/sh/shBrushVb.js
turbogears/widgets/static/sh/shBrushXml.js
turbogears/widgets/static/sh/shCore.js
turbogears/widgets/static/tabber/tabber-minimized.js
turbogears/widgets/static/tabber/tabber.css
turbogears/widgets/static/tabber/tabber.js
turbogears/widgets/static/tabber/tabber_cookie.js
turbogears/widgets/templates/__init__.py
turbogears/widgets/templates/datagrid.kid
turbogears/widgets/templates/paginate_datagrid.kid
turbogears/widgets/tests/__init__.py
turbogears/widgets/tests/form.kid
turbogears/widgets/tests/test_datagrid.py
turbogears/widgets/tests/test_forms.py
turbogears/widgets/tests/test_link_inclusion.py
turbogears/widgets/tests/test_nested_form_controllers.py
turbogears/widgets/tests/test_nested_widgets.py
turbogears/widgets/tests/test_new_validation.py
turbogears/widgets/tests/test_request_related_features.py
turbogears/widgets/tests/test_widgets.py
turbogears/widgets/tests/two_forms.kid
turbogears/widgets/tests/widget.kid
TurboGears-1.0.4.2/TurboGears.egg-info/not-zip-safe0000644000175000017500000000000110710636776020645 0ustar  faidefaide
TurboGears-1.0.4.2/TurboGears.egg-info/top_level.txt0000644000175000017500000000001310745210075021127 0ustar  faidefaideturbogears
TurboGears-1.0.4.2/TurboGears.egg-info/entry_points.txt0000644000175000017500000000302010745210075021674 0ustar  faidefaide
    [console_scripts]
    tg-admin = turbogears.command:main

    [distutils.commands]
    docs = turbogears.docgen:GenSite

    [paste.paster_create_template]
    tgbase = turbogears.command.quickstart:BaseTemplate
    turbogears = turbogears.command.quickstart:TurbogearsTemplate
    tgbig = turbogears.command.quickstart:TGBig
    tgwidget = turbogears.command.quickstart:TGWidgetTemplate

    [turbogears.command]
    quickstart = turbogears.command.quickstart:quickstart
    sql = turbogears.command.base:SQL
    shell = turbogears.command.base:Shell
    toolbox = turbogears.command.base:ToolboxCommand
    update = turbogears.command.quickstart:update
    i18n = turbogears.command.i18n:InternationalizationTool
    info = turbogears.command.info:InfoCommand

    [turbogears.identity.provider]
    sqlobject = turbogears.identity.soprovider:SqlObjectIdentityProvider
    sqlalchemy= turbogears.identity.saprovider:SqlAlchemyIdentityProvider

    [turbogears.extensions]
    identity = turbogears.identity.visitor
    visit = turbogears.visit

    [turbogears.visit.manager]
    sqlobject = turbogears.visit.sovisit:SqlObjectVisitManager
    sqlalchemy = turbogears.visit.savisit:SqlAlchemyVisitManager

    [turbogears.toolboxcommand]
    widgets = turbogears.toolbox.base:WidgetBrowser
    shell = turbogears.toolbox.shell:WebConsole
    admi18n = turbogears.toolbox.admi18n:Internationalization
    designer = turbogears.toolbox.designer:Designer
    info = turbogears.toolbox.base:Info
    catwalk = turbogears.toolbox.catwalk:CatWalk

    TurboGears-1.0.4.2/TurboGears.egg-info/requires.txt0000644000175000017500000000072610745210075021010 0ustar  faidefaideCherryPy >= 2.3.0,<3.0.0alpha
ConfigObj >= 4.3.2
DecoratorTools >= 1.4
FormEncode >= 0.7.1
PasteScript >= 0.9.7,<1.6
RuleDispatch >= 0.5a0.dev-r2303
setuptools >= 0.6c2
simplejson >= 1.3
TurboCheetah >= 1.0
TurboJson >= 1.1.2
TurboKid >= 1.0.4

[sqlobject]
SQLObject>=0.8,<0.10dev

[sqlalchemy]
SQLAlchemy >= 0.3.10

[testtools]
nose >= 0.9.3,<=0.10.0a1

[future]
Elixir>=0.4.0
Genshi>=0.4.4

[exp]
TGFastData

[tgtesttools]
nose >= 0.9.3,<=0.10.0a1
SQLAlchemy >= 0.3.10TurboGears-1.0.4.2/TurboGears.egg-info/PKG-INFO0000644000175000017500000000353210745210075017503 0ustar  faidefaideMetadata-Version: 1.0
Name: TurboGears
Version: 1.0.4.2
Summary: front-to-back rapid web development
Home-page: http://www.turbogears.org
Author: Kevin Dangoor
Author-email: dangoor+turbogears@gmail.com
License: MIT
Download-URL: http://www.turbogears.org/download/filelist.html
Description: Front-to-back rapid web development
        ===================================
        
        TurboGears brings together four major pieces to create an
        easy to install, easy to use web megaframework. It covers
        everything from front end (MochiKit JavaScript for the browser,
        Kid/Genshi/Mako/Cheetah for templates in Python) to the controllers
        (CherryPy) to the back end (SQLAlchemy or SQLObject).
        
        The TurboGears project is focused on providing documentation
        and integration with these tools without losing touch
        with the communities that already exist around those tools.
        
        TurboGears is easy to use for a wide range of web applications.
        
        The latest development version is available in the
        `TurboGears subversion repository`_.
        
        Our `mailing list`_ is lively and helpful, don't hesitate to
        send your questions there, we will try to help you find out
        a solution to your problem.
        
        .. _mailing list:
        http://groups.google.com/group/turbogears
        
        .. _TurboGears subversion repository:
        http://svn.turbogears.org/trunk#egg=turbogears-dev
        
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: Libraries :: Python Modules
TurboGears-1.0.4.2/TurboGears.egg-info/dependency_links.txt0000644000175000017500000000004210745210075022456 0ustar  faidefaidehttp://files.turbogears.org/eggs/
TurboGears-1.0.4.2/setup.cfg0000644000175000017500000000025610745210077014462 0ustar  faidefaide[easy_install]
find_links = http://www.turbogears.org/download/index.html http://peak.telecommunity.com/snapshots

[egg_info]
tag_build = 
tag_date = 0
tag_svn_revision = 0

TurboGears-1.0.4.2/CONTRIBUTORS.txt0000644000175000017500000000247710710636656015355 0ustar  faidefaideTurboGears was founded by Kevin Dangoor. Included projects were
created by Ian Bicking, Remi Delon, Bob Ippolito, Ryan Tomayko and
Fredrik Lundh. Many other people have contributed to the included
projects.

Other TurboGears contributors:
* Elvelind Grandin
* Ronald Jaramillo
* Jeff Watkins
* Dan Jacob
* Max Ischenko
* Simon Belak
* Karl Guertin
* Jared Kuolt
* David Stanek
* Richard Standbrook
* Michele Cella
* Alberto Valverde
* Lee McFadden
* Jorge Godoy
* Ondrej Zara (author of WWW SQL Designer)
* Irmen de Jong (author of Kronos)
* Florent Aide
* Travis Bradshaw
* Mark Ramm
* Felix Schwarz
* Christopher Arndt
* Chris Miles
* Diez B. Roggisch
* Paul Johnston
* Roger Demetrescu
* Christoph Zwerschke

Contributor Statement:

People who contribute non-trivial changes to TurboGears or have
commit access to the repository agree to the following statement:

I, __(your name here)__ agree to the following statements concerning
my contributions to the TurboGears project:

 * These contributions are my own work and that I have full ownership
   authority over them
 * My contributions will be licensed under the same terms as TurboGears
   itself (an "MIT-style" license)
 * I am unaware of any patent or patent application that would prevent
   free use of my contribution

Full legal name and address:
(your name and address here)

TurboGears-1.0.4.2/turbogears/0000755000175000017500000000000010745210077015013 5ustar  faidefaideTurboGears-1.0.4.2/turbogears/config.py0000644000175000017500000001707510710636656016653 0ustar  faidefaideimport sys, os, glob, re

from cherrypy import config
from configobj import ConfigObj
import pkg_resources
import logging
import logging.handlers

__all__ = ["update_config", "get", "update"]

try:
    set
except NameError:
    from sets import Set as set

class ConfigError(Exception):
    pass

def _get_formatters(formatters):
    for key, formatter in formatters.items():
        kw = {}
        fmt = formatter.get("format", None)
        if fmt:
            fmt = fmt.replace("*(", "%(")
            kw["fmt"] = fmt
        datefmt = formatter.get("datefmt", None)
        if datefmt:
            kw["datefmt"] = datefmt
        formatter = logging.Formatter(**kw)
        formatters[key] = formatter

def _get_handlers(handlers, formatters):
    for key, handler in handlers.items():
        kw = {}
        try:
            cls = handler.get("class")
            args = handler.get("args", tuple())
            level = handler.get("level", None)
            try:
                cls = eval(cls, logging.__dict__)
            except NameError:
                try:
                    cls = eval(cls, logging.handlers.__dict__)
                except NameError, err:
                    raise ConfigError("Specified class in handler "
                        "%s is not a recognizable logger name" % key)
            try:
                handler_obj = cls(*eval(args, logging.__dict__))
            except IOError,err:
                raise ConfigError("Missing or wrong argument to "
                    "%s in handler %s -> %s " % (cls.__name__,key,err))
            except TypeError,err:
                raise ConfigError("Wrong format for arguments "
                    "to %s in handler %s -> %s" % (cls.__name__,key,err))
            if level:
                level = eval(level, logging.__dict__)
                handler_obj.setLevel(level)
        except KeyError:
            raise ConfigError("No class specified for logging "
                "handler %s" % key)
        formatter = handler.get("formatter", None)
        if formatter:
            try:
                formatter = formatters[formatter]
            except KeyError:
                raise ConfigError("Handler %s references unknown "
                            "formatter %s" % (key, formatter))
            handler_obj.setFormatter(formatter)
        handlers[key] = handler_obj

def _get_loggers(loggers, handlers):
    for key, logger in loggers.items():
        qualname = logger.get("qualname", None)
        if qualname:
            log = logging.getLogger(qualname)
        else:
            log = logging.getLogger()

        level = logger.get("level", None)
        if level:
            level = eval(level, logging.__dict__)
        else:
            level = logging.NOTSET
        log.setLevel(level)

        propagate = logger.get("propagate", None)
        if propagate is not None:
            log.propagate = propagate

        cfghandlers = logger.get("handlers", None)
        if cfghandlers:
            if isinstance(cfghandlers, basestring):
                cfghandlers = [cfghandlers]
            for handler in cfghandlers:
                try:
                    handler = handlers[handler]
                except KeyError:
                    raise ConfigError("Logger %s references unknown "
                                "handler %s" % (key, handler))
                log.addHandler(handler)

def configure_loggers(config):
    """Configures the Python logging module, using options that are very
    similar to the ones listed in the Python documentation. This also
    removes the logging configuration from the configuration dictionary
    because CherryPy doesn't like it there. Here are some of the Python
    examples converted to the format used here:

    [logging]
    [[loggers]]
    [[[parser]]]
    [logger_parser]
    level="DEBUG"
    handlers="hand01"
    propagate=1
    qualname="compiler.parser"

    [[handlers]]
    [[[hand01]]]
    class="StreamHandler"
    level="NOTSET"
    formatter="form01"
    args="(sys.stdout,)"

    [[formatters]]
    [[[form01]]]
    format="F1 *(asctime)s *(levelname)s *(message)s"
    datefmt=


    One notable format difference is that *() is used in the formatter
    instead of %() because %() is already used for config file
    interpolation.
    """
    if not config.has_key("logging"):
        config["global"]["tg.new_style_logging"] = False
        return
    logcfg = config["logging"]
    formatters = logcfg.get("formatters", {})
    _get_formatters(formatters)

    handlers = logcfg.get("handlers", {})
    _get_handlers(handlers, formatters)

    loggers = logcfg.get("loggers", {})
    _get_loggers(loggers, handlers)

    del config["logging"]
    config["global"]["tg.new_style_logging"] = True

def config_defaults():
    current_dir_uri = os.path.abspath(os.getcwd())
    if not current_dir_uri.startswith("/"):
        current_dir_uri = "/" + current_dir_uri
    defaults = {'current_dir_uri' : current_dir_uri}
    return defaults

def config_obj(configfile = None, modulename = None):
    defaults = config_defaults()
    if modulename:
        mod_globals = dict()
        lastdot = modulename.rfind(".")
        firstdot = modulename.find(".")
        packagename = modulename[:lastdot]
        top_level_package = modulename[:firstdot]

        modname = modulename[lastdot+1:]
        modfile = pkg_resources.resource_filename(packagename,
                                        modname + ".cfg")
        if not os.path.exists(modfile):
            modfile = pkg_resources.resource_filename(packagename,
                                            modname)
        if os.path.isdir(modfile):
            configfiles = glob.glob(os.path.join(modfile, "*.cfg"))
        else:
            configfiles = [modfile]
        configdata = ConfigObj(unrepr=True)
        top_level_dir = pkg_resources.resource_filename(
                        top_level_package, "")[:-1].replace("\\", "/")
        package_dir = pkg_resources.resource_filename(
                        packagename, "")[:-1].replace("\\", "/")
        defaults.update(dict(top_level_dir=top_level_dir,
                             package_dir=package_dir))
        configdata.merge(dict(DEFAULT=defaults))
        for file in configfiles:
            configdata2 = ConfigObj(file, unrepr=True)
            configdata2.merge(dict(DEFAULT=defaults))
            configdata.merge(configdata2)

    if configfile:
        if modulename:
            configdata2 = ConfigObj(configfile, unrepr=True)
            configdata2.merge(dict(DEFAULT=defaults))
            configdata.merge(configdata2)
        else:
            configdata = ConfigObj(configfile, unrepr=True)
    return configdata

def update_config(configfile=None, modulename=None):
    """Updates the system configuration either from a ConfigObj
    (INI-style) config file, a module name specified in dotted notation
    or both (the module name is assumed to have a ".cfg" extension).
    If both are specified, the module is called first,
    followed by the config file. This means that the config file's options
    override the options in the module file."""
    configdict = config_obj(configfile, modulename).dict()
    configure_loggers(configdict)
    config.update(configdict)

def get(key, default_value=None, return_section=False, path=None):
    """Retrieves a config value"""
    value = config.get(key, default_value, return_section, path)
    if value and key == 'sqlobject.dburi' and os.name == "nt":
        value = re.sub('///(\w):', '///\\1|', value)
    return value

def update(configvalues):
    """Updates the configuration with the values from the dictionary."""
    return config.update(configvalues)
TurboGears-1.0.4.2/turbogears/startup.py0000644000175000017500000002516510724562302017076 0ustar  faidefaide"Things to do when TurboGears is imported."

import os
import errno
import logging
import sys
import time
import atexit
import signal

import pkg_resources
import cherrypy
from cherrypy import _cputil
from formencode.variabledecode import NestedVariables
from cherrypy._cpwsgi import wsgiApp, CPHTTPRequest
from cherrypy._cpwsgiserver import CherryPyWSGIServer

from turbogears import config, scheduler, database
from turbogears import view
from turbogears.database import hub_registry, EndTransactionsFilter

log = logging.getLogger("turbogears.startup")

pkg_resources.require("TurboGears")


def reloader_thread(freq):
    """Monkeypatch for the reloader provided by CherryPy.

    This reloader is designed to reload a single package. This is
    more efficient and, more important, compatible with zipped
    libraries that may not provide access to the individual files."""

    def archive_selector(module):
        if hasattr(module, '__loader__'):
            if hasattr(module.__loader__, 'archive'):
                return module.__loader__.archive
        return module

    mtimes = {}
    package = config.get("autoreload.package", None)
    if package is None:
        print \
"""TurboGears requires autoreload.package to be set. It can be an empty
value, which will use CherryPy's default behavior which is to check
every module. Setting an actual package makes the check much faster."""
        return
    while cherrypy.lib.autoreload.RUN_RELOADER:
        if package:
            modnames = filter(lambda modname: modname.startswith(package),
                                sys.modules.keys())
            modlist = [sys.modules[modname] for modname in modnames]
        else:
            modlist = map(archive_selector, sys.modules.values())
        for filename in filter(lambda v: v,
                map(lambda m: getattr(m, "__file__", None), modlist)):
            if filename.endswith(".kid") or filename == "<string>":
                continue
            orig_filename = filename
            if filename.endswith(".pyc"):
                filename = filename[:-1]
            try:
                mtime = os.stat(filename).st_mtime
            except OSError, e:
                if orig_filename.endswith('.pyc') and e[0] == errno.ENOENT:
                    # This prevents us from endlessly restarting
                    # if there is an old .pyc lying around
                    # after a .py file has been deleted
                    try: os.unlink(orig_filename)
                    except: pass
                sys.exit(3) # force reload
            if filename not in mtimes:
                mtimes[filename] = mtime
                continue
            if mtime > mtimes[filename]:
                sys.exit(3) # force reload
        time.sleep(freq)

cherrypy.lib.autoreload.reloader_thread = reloader_thread

webpath = ""

DNS_SD_PID = None


def start_bonjour(package=None):
    global DNS_SD_PID
    if DNS_SD_PID:
        return
    if (not hasattr(cherrypy, "root")) or (not cherrypy.root):
        return
    if not package:
        package = cherrypy.root.__module__
        package = package[:package.find(".")]

    host = config.get('server.socket_host', '')
    port = str(config.get('server.socket_port'))
    env = config.get('server.environment')
    name = package + ": " + env
    type = "_http._tcp"

    cmds = [['/usr/bin/avahi-publish-service', ["-H", host, name, type, port]],
            ['/usr/bin/dns-sd', ['-R', name, type, "."+host, port, "path=/"]]]

    for cmd, args in cmds:
        # TODO:. This check is flawed.  If one has both services installed and
        # avahi isn't the one running, then this won't work.  We should either
        # try registering with both or checking what service is running and use
        # that.  Program availability on the filesystem was never enough...
        if os.path.exists(cmd):
            DNS_SD_PID = os.spawnv(os.P_NOWAIT, cmd, [cmd]+args)
            atexit.register(stop_bonjour)
            break


def stop_bonjour():
    if not DNS_SD_PID:
        return
    try:
        os.kill(DNS_SD_PID, signal.SIGTERM)
    except OSError:
        pass


class VirtualPathFilter(object):
    """Filter that makes CherryPy ignorant of a URL root path.

    That is, you can mount your app so the URI "/users/~rdel/myapp/"
    maps to the root object "/".
    """

    def on_start_resource(self):
        prefix = config.get('server.webpath', False)
        if prefix:
            path = cherrypy.request.object_path
            if path == prefix:
                cherrypy.request.object_path = '/'
            elif path.startswith(prefix):
                cherrypy.request.object_path = path[len(prefix):]
            else:
                raise cherrypy.NotFound(path)

class NestedVariablesFilter(object):

    def before_main(self):
        if hasattr(cherrypy.request, "params"):
            cherrypy.request.params = \
                NestedVariables.to_python(cherrypy.request.params or {})


def startTurboGears():
    """Handles TurboGears tasks when the CherryPy server starts.

    This adds the "tg_js" configuration to make MochiKit accessible.
    It also turns on stdlib logging when in development mode.
    """
    config.update({"/tg_static":
            {
            "static_filter.on": True,
            "static_filter.dir":
                os.path.abspath(pkg_resources.resource_filename(__name__, "static")),
            'log_debug_info_filter.on' : False,
            }
        })
    config.update({"/tg_js" :
            {
            "static_filter.on" : True,
            "static_filter.dir" :
                os.path.abspath(pkg_resources.resource_filename(__name__, "static/js")),
            'log_debug_info_filter.on' : False,
            }
        })
    cherrypy.config.environments['development']['log_debug_info_filter.on'] = False

    if config.get("decoding_filter.on", path="/") is None:
        config.update({"/": {
            "decoding_filter.on" : True,
            "decoding_filter.encoding" : config.get(
                                        "kid.encoding", "utf8")
        }})

    view.load_engines()
    view.loadBaseTemplates()
    global webpath
    webpath = config.get("server.webpath", "")

    if hasattr(cherrypy, "root") and cherrypy.root:
        if not hasattr(cherrypy.root, "_cp_filters"):
            cherrypy.root._cp_filters= []
        morefilters = [EndTransactionsFilter(),
                       NestedVariablesFilter()]
        if webpath:
            morefilters.insert(0, VirtualPathFilter())
        cherrypy.root._cp_filters.extend(morefilters)

    if webpath.startswith("/"):
        webpath = webpath[1:]
    if webpath and not webpath.endswith("/"):
        webpath = webpath + "/"
    isdev = config.get('server.environment') == 'development'
    if not config.get("tg.new_style_logging"):
        if config.get('server.log_to_screen'):
            setuplog = logging.getLogger()
            setuplog.setLevel(logging.DEBUG)
            fmt = logging.Formatter("%(asctime)s %(name)s "
                                    "%(levelname)s %(message)s")
            handler = logging.StreamHandler(sys.stdout)
            handler.setLevel(logging.DEBUG)
            handler.setFormatter(fmt)
            setuplog.addHandler(handler)

        logfile = config.get("server.log_file")
        if logfile:
            setuplog = logging.getLogger("turbogears.access")
            setuplog.propagate = 0
            fmt = logging.Formatter("%(message)s")
            handler = logging.FileHandler(logfile)
            handler.setLevel(logging.INFO)
            handler.setFormatter(fmt)
            setuplog.addHandler(handler)

    bonjoursetting = config.get("tg.bonjour", None)
    if bonjoursetting or isdev:
        start_bonjour(bonjoursetting)

    if config.get("sqlalchemy.dburi"):
        database.get_engine()

    # Start all TurboGears extensions
    extensions = pkg_resources.iter_entry_points("turbogears.extensions")
    for entrypoint in extensions:
        ext = entrypoint.load()
        if hasattr(ext, "start_extension"):
            ext.start_extension()

    for item in call_on_startup:
        item()

    if config.get("tg.scheduler", False):
        scheduler._start_scheduler()
        log.info("Scheduler started")


def stopTurboGears():
    # end all transactions and clear out the hubs to
    # help ensure proper reloading in autoreload situations
    for hub in hub_registry:
        hub.end()
    hub_registry.clear()

    stop_bonjour()

    # Shut down all TurboGears extensions
    extensions= pkg_resources.iter_entry_points( "turbogears.extensions" )
    for entrypoint in extensions:
        ext= entrypoint.load()
        if hasattr(ext, "shutdown_extension"):
            ext.shutdown_extension()

    for item in call_on_shutdown:
        item()

    if config.get("tg.scheduler", False):
        scheduler._stop_scheduler()
        log.info("Scheduler stopped")

old_object_trail = _cputil.get_object_trail

# hang on to object trail to use it to find an app root if need be
def get_object_trail(object_path=None):
    trail = old_object_trail(object_path)
    try:
        cherrypy.request.object_trail = trail
    except AttributeError:
        pass
    return trail

_cputil.get_object_trail = get_object_trail

class SimpleWSGIServer(CherryPyWSGIServer):
    """A WSGI server that accepts a WSGI application as a parameter."""
    RequestHandlerClass = CPHTTPRequest

    def __init__(self):
        conf = cherrypy.config.get
        wsgi_app = wsgiApp
        if conf('server.environment') == 'development':
            try:
                from paste.evalexception.middleware import EvalException
            except ImportError:
                pass
            else:
                wsgi_app = EvalException(wsgi_app, global_conf={})
                cherrypy.config.update({'server.throw_errors':True})
        bind_addr = (conf("server.socket_host"), conf("server.socket_port"))
        CherryPyWSGIServer.__init__(self, bind_addr, wsgi_app,
                                    conf("server.thread_pool"),
                                    conf("server.socket_host"),
                                    request_queue_size = conf(
                                        "server.socket_queue_size"),
                                    )

def start_server(root):
    cherrypy.root = root
    if config.get("tg.fancy_exception", False):
        cherrypy.server.start(server=SimpleWSGIServer())
    else:
        cherrypy.server.start()

if startTurboGears not in cherrypy.server.on_start_server_list:
    cherrypy.server.on_start_server_list.append(startTurboGears)

if stopTurboGears not in cherrypy.server.on_stop_server_list:
    cherrypy.server.on_stop_server_list.append(stopTurboGears)

call_on_startup = []
call_on_shutdown = []
TurboGears-1.0.4.2/turbogears/genericfunctions.py0000644000175000017500000000470510710636656020747 0ustar  faidefaideimport sys
from itertools import izip, repeat, chain as ichain

from dispatch import strategy, functions


class MultiorderGenericFunction(functions.GenericFunction):

    """Generic function allowing a priori method ordering."""

    def __init__(self, func):
        functions.GenericFunction.__init__(self, func)
        self.order_when = []
        self.order_around = []

    def when(self, cond, order=0):
        if order not in self.order_when:
            self.order_when.append(order)
            self.order_when.sort()
        return self._decorate(cond, "primary%d" % order)

    def around(self, cond, order=0):
        if order not in self.order_around:
            self.order_around.append(order)
            self.order_around.sort()
        return self._decorate(cond, "around%d" % order)

    # Based on dispatch.functions.GenericFunction.combine
    def combine(self, cases):
        strict = [strategy.ordered_signatures,strategy.safe_methods]
        loose  = [strategy.ordered_signatures,strategy.all_methods]

        primary_names = ['primary%d' % order for order in self.order_when]
        around_names = ['around%d' % order for order in self.order_around]

        cases = strategy.separate_qualifiers(
            cases,
            before = loose, after =loose,
            **dict(izip(ichain(primary_names, around_names), repeat(strict)))
        )

        primary = strategy.method_chain(ichain(
                    *[cases.get(primary, []) for primary in primary_names]))

        if cases.get('after') or cases.get('before'):

            befores = strategy.method_list(cases.get('before',[]))
            afters = strategy.method_list(list(cases.get('after',[]))[::-1])

            def chain(*args,**kw):
                for tmp in befores(*args,**kw): pass  # toss return values
                result = primary(*args,**kw)
                for tmp in afters(*args,**kw): pass  # toss return values
                return result

        else:
            chain = primary

        if (self.order_around):
            chain = strategy.method_chain(ichain(*([cases.get(around, [])
                                    for around in around_names] + [[chain]])))

        return chain


def getter(var):
    """Create an accessor for given variable."""
    frame = sys._getframe(1)
    return lambda: var in frame.f_locals and frame.f_locals[var] or \
                                             frame.f_globals[var]

__all__ = ["MultiorderGenericFunction", "getter", ]
TurboGears-1.0.4.2/turbogears/controllers.py0000644000175000017500000005411010724562302017732 0ustar  faidefaide"""Classes and methods for TurboGears controllers."""
import logging
import re
import urllib
import types
from itertools import izip
import cherrypy
from dispatch import generic, strategy, functions
import turbogears.util as tg_util
import turbogears
from inspect import isclass
from turbogears import view, database, errorhandling, config
from turbogears.decorator import weak_signature_decorator
from turbogears.validators import Invalid
from turbogears.errorhandling import error_handler, exception_handler

log = logging.getLogger("turbogears.controllers")

unicodechars = re.compile(r"([^\x00-\x7F])")

if config.get("session_filter.on", None) == True:
    if config.get("session_filter.storage_type", None) == "PostgreSQL":
        import psycopg2
        config.update(
                {'session_filter.get_db': psycopg2.connect(
                    psycopg2.get('sessions.postgres.dsn'))
                    })
    # support for mysql/sqlite/etc here

def _process_output(output, template, format, content_type,
        mapping, fragment=False):
    """
    Produces final output form from the data returned from a
    controller method.

    See the expose() arguments for more info in theses ones since
    they are the same.
    """
    if isinstance(output, dict):
        from turbogears.widgets import js_location

        css = tg_util.setlike()
        js = dict(izip(js_location, iter(tg_util.setlike, None)))
        include_widgets = {}
        include_widgets_lst = config.get("tg.include_widgets", [])

        if config.get("tg.mochikit_all", False):
            include_widgets_lst.insert(0, 'turbogears.mochikit')

        for i in include_widgets_lst:
            widget = tg_util.load_class(i)
            if isclass(widget):
                widget = widget()
            include_widgets["tg_%s" % i.split(".")[-1]] = widget
            for script in widget.retrieve_javascript():
                if hasattr(script, "location"):
                    js[script.location].add(script)
                else:
                    js[js_location.head].add(script)
            css.add_all(widget.retrieve_css())

        for value in output.itervalues():
            if hasattr(value, "retrieve_css"):
                retrieve = getattr(value, "retrieve_css")
                if callable(retrieve):
                    css.add_all(value.retrieve_css())
            if hasattr(value, "retrieve_javascript"):
                retrieve = getattr(value, "retrieve_javascript")
                if callable(retrieve):
                    for script in value.retrieve_javascript():
                        if hasattr(script, "location"):
                            js[script.location].add(script)
                        else:
                            js[js_location.head].add(script)
        output.update(include_widgets)
        output["tg_css"] = css
        #output.update([("tg_js_%s" % str(l), js[l]) for l in js_location])
        for l in iter(js_location):
            output["tg_js_%s" % str(l)] = js[l]

        tg_flash = _get_flash()
        if not tg_flash == None:
            output["tg_flash"] = tg_flash
        output = view.render(output, template=template, format=format,
                    mapping=mapping, content_type=content_type,
                    fragment=fragment)
    else:
        if content_type:
            cherrypy.response.headers["Content-Type"] = content_type

    # fix the Safari XMLHttpRequest encoding problem
    try:
        contentType = cherrypy.response.headers["Content-Type"]
        ua = cherrypy.request.headers["User-Agent"]
    except KeyError:
        return output
    if not contentType.startswith("text/"):
        return output
    ua = view.UserAgent(ua)
    enc = tg_util.get_template_encoding_default()
    if ua.browser == "safari":
        if isinstance(output, str):
            output = output.decode(enc)
        elif isinstance(output, types.GeneratorType):
            output = "".join(output)

        output = unicodechars.sub(
            lambda m: "&#x%x;" % ord(m.group(1)), output).encode("ascii")

    if isinstance(output, unicode):
        output = output.encode(enc)
    return output

class BadFormatError(Exception):
    """Output-format exception."""

def validate(form=None, validators=None,
             failsafe_schema=errorhandling.FailsafeSchema.none,
             failsafe_values=None, state_factory=None):
    """Validate input.

    @param form form to validate input from
    @param validators individual validators to use for parameters
    @param failsafe_schema fail-safe schema
    @param failsafe_values replacements for erroneous inputs
    @param state_factory callable which returns the initial state instance for
           validation
    """
    def entangle(func):
        recursion_guard = dict(func=func)
        if callable(form) and not hasattr(form, "validate"):
            init_form = lambda self: form(self)
        else:
            init_form = lambda self: form

        def validate(func, *args, **kw):
            if tg_util.call_on_stack("validate", recursion_guard, 4):
                return func(*args, **kw)
            form = init_form(args and args[0] or kw["self"])
            args, kw = tg_util.to_kw(func, args, kw)

            errors = {}
            if state_factory is not None:
                state = state_factory()
            else:
                state = None

            if form:
                value = kw.copy()
                try:
                    kw.update(form.validate(value, state))
                except Invalid, e:
                    errors = e.unpack_errors()
                    cherrypy.request.validation_exception = e
                cherrypy.request.validated_form = form

            if validators:
                if isinstance(validators, dict):
                    for field, validator in validators.iteritems():
                        try:
                            kw[field] = validator.to_python(
                                kw.get(field, None), state
                                )
                        except Invalid, error:
                            errors[field] = error
                else:
                    try:
                        value = kw.copy()
                        kw.update(validators.to_python(value, state))
                    except Invalid, e:
                        errors = e.unpack_errors()
                        cherrypy.request.validation_exception = e
            cherrypy.request.validation_errors = errors
            cherrypy.request.input_values = kw.copy()
            cherrypy.request.validation_state = state

            if errors:
                kw = errorhandling.dispatch_failsafe(failsafe_schema,
                                            failsafe_values, errors, func, kw)
            args, kw = tg_util.from_kw(func, args, kw)
            return errorhandling.run_with_errors(errors, func, *args, **kw)

        return validate
    return weak_signature_decorator(entangle)

class CustomDispatch(functions.GenericFunction):

    def combine(self,cases):
        strict = [strategy.ordered_signatures,strategy.safe_methods]
        cases = strategy.separate_qualifiers(
            cases,
            primary = strict,
        )
        primary = strategy.method_chain(cases.get('primary',[]))
        if type(primary) != types.FunctionType:
            for i in primary:
                for y in i:
                    return y[1]
        return primary

def _add_rule(_expose, found_default, as_format, accept_format, template,
              rulefunc):
    if as_format == "default":
        if found_default:
            colon = template.find(":")
            if colon == -1:
                as_format = template
            else:
                as_format = template[:colon]
        else:
            found_default = True
    ruleparts = []
    ruleparts.append('kw.get("tg_format", "default") == "%s"'
                % as_format)
    if accept_format:
        ruleparts.append('(accept == "%s" and kw.get("tg_format", '
                '"default") == "default")' % accept_format)
    rule = " or ".join(ruleparts)
    log.debug("Generated rule %s", rule)
    _expose.when(rule)(rulefunc)

    return found_default

def _build_rules(func):
    def _expose(func, accept, allow_json, *args, **kw):
        pass
    _expose = generic(CustomDispatch)(_expose)

    if func._allow_json:
        log.debug("Adding allow_json rule: "
            'allow_json and '
            '(kw.get("tg_format", None) == "json" or accept '
            '=="text/javascript")')
        _expose.when('allow_json '
            'and (kw.get("tg_format", None) == "json" or accept'
            ' =="text/javascript")')(
            lambda _func, accept, allow_json,
                *args, **kw: _execute_func(
                    _func, "json", None, None, None, False, args, kw))

    found_default = False
    for ruleinfo in func._ruleinfo:
        found_default = _add_rule(_expose, found_default, **ruleinfo)

    func._expose = _expose

def expose(template=None, validators=None, allow_json=None, html=None,
           format=None, content_type=None, inputform=None, fragment=False,
           as_format="default", mapping=None, accept_format=None):
    """Exposes a method to the web.

    By putting the expose decorator on a method, you tell TurboGears that
    the method should be accessible via URL traversal. Additionally, expose
    handles the output processing (turning a dictionary into finished
    output) and is also responsible for ensuring that the request is
    wrapped in a database transaction.

    You can apply multiple expose decorators to a method, if
    you'd like to support multiple output formats. The decorator that's
    listed first in your code without as_format or accept_format is
    the default that is chosen when no format is specifically asked for.
    Any other expose calls that are missing as_format and accept_format
    will have as_format implicitly set to the whatever comes before
    the ":" in the template name (or the whole template name if there
    is no ":". For example, <code>expose("json")</code>, if it's not
    the default expose, will have as_format set to "json".

    When as_format is set, passing the same value in the tg_format
    parameter in a request will choose the options for that expose
    decorator. Similarly, accept_format will watch for matching
    Accept headers. You can also use both. expose("json", as_format="json",
    accept_format="text/javascript") will choose JSON output for either
    case: tg_format=json as a parameter or Accept: text/javascript as a
    request header.

    Passing allow_json=True to an expose decorator
    is equivalent to adding the decorator just mentioned.

    Each expose decorator has its own set of options, and each one
    can choose a different template or even template engine (you can
    use Kid for HTML output and Cheetah for plain text, for example).
    See the other expose parameters below to learn about the options
    you can pass to the template engine.

    Take a look at the
    <a href="tests/test_expose-source.html">test_expose.py</a> suite
    for more examples.

    @param template "templateengine:dotted.reference" reference along the
            Python path for the template and the template engine. For
            example, "kid:foo.bar" will have Kid render the bar template in
            the foo package.
    @keyparam format format for the template engine to output (if the
            template engine can render different formats. Kid, for example,
            can render "html", "xml" or "xhtml")
    @keyparam content_type sets the content-type http header
    @keyparam allow_json allow the function to be exposed as json
    @keyparam fragment for template engines (like Kid) that generate
            DOCTYPE declarations and the like, this is a signal to
            just generate the immediate template fragment. Use this
            if you're building up a page from multiple templates or
            going to put something onto a page with .innerHTML.
    @keyparam mapping mapping with options that are sent to the template
            engine
    @keyparam as_format designates which value of tg_format will choose
            this expose.
    @keyparam accept_format which value of an Accept: header will
            choose this expose.
    @keyparam html deprecated in favor of template
    @keyparam validators deprecated. Maps argument names to validator
            applied to that arg
    @keyparam inputform deprecated. A form object that generates the
            input to this method
    """
    if html:
        template = html
    if not template:
        template = format
    if format == "json" or (format == None and template == None):
        template = "json"
        allow_json = True
    if content_type is None:
        content_type = config.get("tg.content_type", None)

    if config.get("tg.session.automatic_lock",None) == True:
        cherrypy.session.acquire_lock()

    def entangle(func):
        log.debug("Exposing %s", func)
        log.debug("template: %s, format: %s, allow_json: %s, "
            "content-type: %s", template, format, allow_json, content_type)
        if not getattr(func, "exposed", False):
            def expose(func, *args, **kw):
                accept = cherrypy.request.headers.get('Accept', "").lower()
                if not hasattr(func, "_expose"):
                    _build_rules(func)
                if hasattr(cherrypy.request, "in_transaction"):
                    output = func._expose(func, accept, func._allow_json,
                                *args, **kw)
                else:
                    cherrypy.request.in_transaction = True
                    output = database.run_with_transaction(
                            func._expose, func, accept, func._allow_json,
                            *args, **kw)
                return output
            func.exposed = True
            func._ruleinfo = []
            allow_json_from_config = config.get(
                                        "tg.allow_json", False)
            func._allow_json = allow_json_from_config
        else:
            expose = lambda func, *args, **kw: func(*args, **kw)

        func._ruleinfo.insert(0, dict(as_format = as_format,
            accept_format = accept_format, template = template,
            rulefunc = lambda _func, accept, allow_json,
                    *args, **kw:
                    _execute_func(_func, template, format, content_type,
                                mapping, fragment, args, kw)))

        if allow_json:
            func._allow_json = True

        if inputform or validators:
            import warnings
            warnings.warn(
                "Use a separate decorator validate() rather than passing "
                "arguments validators and/or inputform to decorator "
                "expose().",
                DeprecationWarning, 2)
            func = validate(form=inputform, validators=validators)(func)

        return expose
    return weak_signature_decorator(entangle)

def _execute_func(func, template, format, content_type, mapping, fragment, args, kw):
    """Call controller method and process it's output."""
    if config.get("tg.strict_parameters", False):
        tg_util.remove_keys(kw, ["tg_random", "tg_format"])
    else:
        args, kw = tg_util.adapt_call(func, args, kw)
    if config.get('server.environment', 'development') == 'development':
        # Only output this in development mode: If it's a field storage object,
        # this means big memory usage, and we don't want that in production
        log.debug("Calling %s with *(%s), **(%s)", func, args, kw)
    output = errorhandling.try_call(func, *args, **kw)
    if isinstance(output, list):
        return output
    assert isinstance(output, basestring) or isinstance(output, dict) \
        or isinstance(output, types.GeneratorType), \
           "Method %s.%s() returned unexpected output. Output should " \
           "be of type basestring, dict or generator." % (
            args[0].__class__.__name__, func.__name__)
    if isinstance(output, dict):
        template = output.pop("tg_template", template)
        format= output.pop("tg_format", format)
    if template and template.startswith("."):
        template = func.__module__[:func.__module__.rfind('.')]+template
    return _process_output(output, template, format, content_type, mapping, fragment)

def flash(message):
    """Set a message to be displayed in the browser on next page display."""
    cherrypy.response.simple_cookie['tg_flash'] = tg_util.to_utf8(message)
    cherrypy.response.simple_cookie['tg_flash']['path'] = '/'

def _get_flash():
    """Retrieve the flash message (if one is set), clearing the message."""
    request_cookie = cherrypy.request.simple_cookie
    response_cookie = cherrypy.response.simple_cookie

    def clearcookie():
        response_cookie["tg_flash"] = ""
        response_cookie["tg_flash"]['expires'] = 0
        response_cookie['tg_flash']['path'] = '/'

    if response_cookie.has_key("tg_flash"):
        message = response_cookie["tg_flash"].value
        response_cookie.pop("tg_flash")
        if request_cookie.has_key("tg_flash"):
            # New flash overrided old one sitting in cookie. Clear that old cookie.
            clearcookie()
    elif request_cookie.has_key("tg_flash"):
        message = request_cookie["tg_flash"].value
        if not response_cookie.has_key("tg_flash"):
            clearcookie()
    else:
        message = None
    if message:
        message = unicode(message, 'utf-8')
    return message

class Controller(object):
    """Base class for a web application's controller.

    Currently, this provides positional parameters functionality
    via a standard default method.
    """

class RootController(Controller):
    """Base class for the root of a web application.

    Your web application should have one of these. The root of
    your application is used to compute URLs used by your app.
    """
    is_app_root = True

    msglog = logging.getLogger('cherrypy.msg')
    msglogfunc = {0: msglog.info, 1: msglog.warning, 2: msglog.error}
    def _cp_log_message(self, msg, context = 'nocontext', severity = 0):
        log = self.msglogfunc[severity]
        text = ''.join((context, ': ', msg))
        log(text)

    accesslog = logging.getLogger("turbogears.access")
    def _cp_log_access(self):
        tmpl = '%(h)s %(l)s %(u)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
        try:
            username = cherrypy.request.user_name
            if not username:
                username = "-"
        except AttributeError:
            username = "-"
        s = tmpl % {'h': cherrypy.request.remote_host,
                   'l': '-',
                   'u': username,
                   'r': cherrypy.request.requestLine,
                   's': cherrypy.response.status.split(" ", 1)[0],
                   'b': cherrypy.response.headers.get('Content-Length',
                            '') or "-",
                   'f': cherrypy.request.headers.get('referer', ''),
                   'a': cherrypy.request.headers.get('user-agent', ''),
        }
        self.accesslog.info(s)

Root = RootController

def url(tgpath, tgparams=None, **kw):
    """Computes URLs.

    tgpath can be a list or a string. If the path is absolute (starts
    with a "/"), the server.webpath and the approot of the application
    are prepended to the path. In order for the approot to be
    detected properly, the root object should extend
    controllers.RootController.

    Query parameters for the URL can be passed in as a dictionary in
    the second argument *or* as keyword parameters.

    Values which are a list or a tuple are used to create multiple
    key-value pairs.
    """
    if not isinstance(tgpath, basestring):
        tgpath = "/".join(list(tgpath))
    if tgpath.startswith("/"):
        if tg_util.request_available():
            check_app_root()
            tgpath = cherrypy.request.app_root + tgpath
        result = config.get("server.webpath", "") + tgpath
    else:
        result = tgpath
    if tgparams is None:
        tgparams = kw
    else:
        try:
            tgparams = tgparams.copy()
            tgparams.update(kw)
        except AttributeError:
            raise TypeError('url() expects a dictionary for query parameters')
    args = []
    for key, value in tgparams.iteritems():
        if value is None:
            continue

        if isinstance(value, (list, tuple)):
            pairs = [(key, v) for v in value]
        else:
            pairs = [(key, value)]

        for (k,v) in pairs:
            if v is None:
                continue

            if isinstance(v, unicode):
                v = v.encode("utf8")

            args.append("%s=%s" % (k, urllib.quote(str(v))))

    if args:
        result += "?" + "&".join(args)

    return result

def check_app_root():
    """Sets cherrypy.request.app_root if needed."""
    if hasattr(cherrypy.request, "app_root"):
        return
    found_root = False
    trail = cherrypy.request.object_trail
    top = len(trail) - 1
    # compute the app_root by stepping back through the object
    # trail and collecting up the path elements after the first
    # root we find
    # we can eliminate this if we find a way to use
    # CherryPy's mounting mechanism whenever a new root
    # is hit.
    rootlist = []
    for i in xrange(len(trail) - 1, -1, -1):
        path, obj = trail[i]
        if not found_root and isinstance(obj, RootController):
            if i == top:
                break
            found_root = True
        if found_root and i > 0:
            rootlist.insert(0, path)
    app_root = "/".join(rootlist)
    if not app_root.startswith("/"):
        app_root = "/" + app_root
    if app_root.endswith("/"):
        app_root = app_root[:-1]
    cherrypy.request.app_root = app_root

def redirect(redirect_path, redirect_params=None, **kw):
    """
    Redirect (via cherrypy.HTTPRedirect).
    Raises the exception instead of returning it, this to allow
    users to both call it as a function or to raise it as an exception.
    """
    raise cherrypy.HTTPRedirect(
                    url(tgpath=redirect_path, tgparams=redirect_params, **kw))

__all__ = ["expose", "validate", "redirect", "flash",
           "Root", "RootController", "Controller",
           "error_handler", "exception_handler",
          ]
TurboGears-1.0.4.2/turbogears/paginate.py0000644000175000017500000004631110742747733017175 0ustar  faidefaideimport re
import types
from math import ceil
import logging
import warnings

import cherrypy
try:
    import sqlobject
    from sqlobject.main import SelectResults
except ImportError:
    SelectResults = None
    sqlobject = None

try:
    # Can't depend on sqlalchemy being available.
    import sqlalchemy
    from sqlalchemy.ext.selectresults import SelectResults as SASelectResults
    from sqlalchemy.orm.query import Query
    # sqlalchemy 0.4.x turned SelectResults into a function that returns a Query
    if isinstance(SASelectResults, types.FunctionType):
        SASelectResults = None
except ImportError:
    SASelectResults = None
    sqlalchemy = None
    Query = None

import turbogears
from turbogears.controllers import redirect
from turbogears.decorator import weak_signature_decorator
from turbogears.view import variable_providers
from formencode.variabledecode import variable_encode, variable_decode

log = logging.getLogger("turbogears.paginate")

# lists of databases that lack support for OFFSET
# this will need to be updated periodically as modules change
_so_no_offset = 'mssql maxdb sybase'.split()
_sa_no_offset = 'mssql maxdb access'.split()

# this is a global that is set the first time paginate() is called
_simulate_offset = None

def paginate(var_name, default_order='', default_reversed=None, limit=10,
            allow_limit_override=False, max_pages=5, dynamic_limit=None):
    '''
    @param var_name: the variable name that the paginate decorator will try
    to control. This key must be present in the dictionnary returned from
    your controller in order for the paginate decorator to be able to handle
    it.
    @type var_name: string

    @param default_order: Needs work! XXX
    @type default_order: string or a list of strings. Any string starting with
    "-" (dash) indicates a reverse order for that field/column.

    @param default_reversed: Needs work! XXX
    @type default_reversed: Boolean [Deprecated]

    @param limit: the hard coded limit that the paginate decorator will
    impose on the number of "var_name" to display at the same time.
    This value can be overridden by the use of the dynamic_limit keyword
    argument
    @type limit: integer

    @param allow_limit_override: A boolean that indicates if the parameters
    passed in the calling URL can modify the imposed limit. By default it is
    set to False. If you want to be able to control the limit by using an
    URL parameter then you need to set this to True.
    @type allow_limit_override: Boolean

    @param max_pages: Needs work! XXX
    @type max_pages: integer

    @param dynamic_limit: If specified, this parameter must be the name
    of a key present in the dictionnary returned by your decorated
    controller. The value found for this key will be used as the limit
    for our pagination and will override the other settings, the hard-coded
    one declared in the decorator itself AND the URL parameter one.
    This enables the programmer to store a limit settings inside the
    application preferences and then let the user manage it.
    @type dynamic_limit: string
    '''
    def entangle(func):
        def decorated(func, *args, **kw):
            def kwpop(default, *names):
                for name in names:
                    if kw.has_key(name):
                        return kw.pop(name)
                return default

            if default_reversed is not None:
                warnings.warn(
                    "default_reversed is deprecated. Use default_order='-field'"
                    " to indicate default reversed order or"
                    " default_order=['field1', '-field2, 'field3']"
                    " for multiple fields", DeprecationWarning, 2)

            get = turbogears.config.get

            page = kwpop(1, var_name + '_tgp_no', 'tg_paginate_no')
            if page == 'last':
                page = None
            else:
                page = int(page)
                if page < 1:
                    page = 1
                    if get('paginate.redirect_on_out_of_range'):
                        cherrypy.request.params[var_name + '_tgp_no'] = page
                        redirect(cherrypy.request.path, cherrypy.request.params)

            limit_ = int(
                kwpop(limit, var_name + '_tgp_limit', 'tg_paginate_limit'))
            order = kwpop(None, var_name + '_tgp_order', 'tg_paginate_order')
            ordering = kwpop(
                {},
                var_name + '_tgp_ordering',
                'tg_paginate_ordering')

            # Convert ordering str to a dict.
            if ordering:
                ordering = convert_ordering(ordering)

            if not allow_limit_override:
                limit_ = limit

            log.debug("Pagination params: page=%s, limit=%s, order=%s "
                      "", page, limit_, order)

            # get the output from the decorated function
            output = func(*args, **kw)
            if not isinstance(output, dict):
                return output
            try:
                var_data = output[var_name]
            except KeyError:
                raise StandardError("Didn't get expected variable")

            if dynamic_limit:
                try:
                    dyn_limit = output[dynamic_limit]
                except KeyError:
                    msg = "dynamic_limit: %s not found in output dict" % (
                            dynamic_limit)
                    raise StandardError(msg)

                limit_ = dyn_limit

            if order and not default_order:
                msg = "If you want to enable ordering you need "
                msg += "to provide a default_order"
                raise StandardError(msg)

            elif default_order and not ordering:
                if isinstance(default_order, basestring):
                    # adapt old style to new style
                    df = [(default_reversed and "-" or "") + default_order]
                elif default_reversed:
                    raise StandardError("default_reversed (deprecated) is only "
                        " allowed when default_order is basestring type")
                else:
                    df = default_order

                ordering = dict([(v.lstrip('-'), [k, not v.startswith('-')])
                                 for k,v in enumerate(df)])
            elif ordering and order:
                sort_ordering(ordering, order)
            log.debug('ordering %s' % ordering)

            row_count = 0
            if (SelectResults and isinstance(var_data, SelectResults)) or \
               (SASelectResults and isinstance(var_data, SASelectResults)) or \
               (Query and isinstance(var_data, Query)):
                row_count = var_data.count() or 0
                if ordering:
                    # Build order_by list.
                    order_cols = range(len(ordering))
                    for (colname, order_opts) in ordering.items():
                        col = sql_get_column(colname, var_data)
                        if not col:
                            msg = "The order column (%s) doesn't exist" % colname
                            raise StandardError(msg)

                        order_by_expr = sql_order_col(col, order_opts[1])
                        order_cols[order_opts[0]] = order_by_expr
                    # May need to address potential of ordering already
                    # existing in var_data.
                    # SO and SA differ on this method name.
                    if hasattr(var_data, 'orderBy'):
                        var_data = var_data.orderBy(order_cols)
                    else:
                        var_data = var_data.order_by(order_cols)

            elif isinstance(var_data, list) or (sqlalchemy and isinstance(
                    var_data, sqlalchemy.orm.attributes.InstrumentedList)):
                row_count = len(var_data)

            else:
                raise StandardError(
                    'Variable is not a list or SelectResults or Query (%s)' % type(
                            var_data))

            # If limit is zero then return all our rows
            if not limit_:
                limit_ = row_count or 1

            page_count = int(ceil(float(row_count)/limit_))

            if page > page_count:
                page = max(page_count, 1)
                if get('paginate.redirect_on_out_of_range'):
                    cherrypy.request.params[var_name + '_tgp_no'] = page
                    redirect(cherrypy.request.path, cherrypy.request.params)

            if page is None:
                page = max(page_count, 1)
                if get('paginate.redirect_on_last_page'):
                    cherrypy.request.params[var_name + '_tgp_no'] = page
                    redirect(cherrypy.request.path, cherrypy.request.params)

            offset = (page-1) * limit_

            # if it's possible display every page
            if page_count <= max_pages:
                pages_to_show = range(1,page_count+1)
            else:
                pages_to_show = _select_pages_to_show(page_count=page_count,
                                              current_page=page,
                                              max_pages=max_pages)

            # which one should we use? cherrypy.request.input_values or kw?
            #input_values = cherrypy.request.input_values.copy()
            ##input_values = kw.copy()
            input_values =  variable_encode(cherrypy.request.params.copy())
            input_values.pop('self', None)
            for input_key in input_values.keys():
                if input_key.startswith(var_name + '_tgp_') or \
                    input_key.startswith('tg_paginate'):
                    del input_values[input_key]

            paginate_instance = Paginate(
                current_page=page,
                limit=limit_,
                pages=pages_to_show,
                page_count=page_count,
                input_values=input_values,
                order=order,
                ordering=ordering,
                row_count=row_count,
                var_name=var_name)

            cherrypy.request.paginate = paginate_instance
            if not hasattr(cherrypy.request, 'paginates'):
                cherrypy.request.paginates = dict()
            cherrypy.request.paginates[var_name] = paginate_instance

            # we replace the var with the sliced one
            endpoint = offset + limit_
            log.debug("slicing data between %d and %d", offset, endpoint)

            global _simulate_offset
            if _simulate_offset is None:
                _simulate_offset = get('paginate.simulate_offset', None)
                if _simulate_offset is None:
                    _simulate_offset = False
                    so_db = get('sqlobject.dburi', 'NOMATCH:').split(':', 1)[0]
                    sa_db = get('sqlalchemy.dburi', 'NOMATCH:').split(':', 1)[0]
                    if so_db in _so_no_offset or sa_db in _sa_no_offset:
                        _simulate_offset = True
                        log.warning("simulating OFFSET, paginate may be slow")
                        log.warning("to turn off, set "
                                     "paginate.simulate_offset=False")

            if _simulate_offset:
                var_data_iter = iter(var_data[:endpoint])
                # skip over the number of records specified by offset
                for i in range(offset):
                    var_data_iter.next()
                # return the records that remain
                output[var_name] = list(var_data_iter)
            else:
                output[var_name] = var_data[offset:endpoint]

            return output
        return decorated
    return weak_signature_decorator(entangle)

def _paginate_var_provider(d):
    # replaced cherrypy.thread_data for cherrypy.request
    # thanks alberto!
    paginate = getattr(cherrypy.request, 'paginate', None)
    if paginate:
        d.update(dict(paginate=paginate))
    paginates = getattr(cherrypy.request, 'paginates', None)
    if paginates:
        d.update(dict(paginates=paginates))
variable_providers.append(_paginate_var_provider)

class Paginate:
    """class for variable provider"""
    def __init__(self, current_page, pages, page_count, input_values,
                 limit, order, ordering, row_count, var_name):

        self.var_name = var_name
        self.pages = pages
        self.limit = limit
        self.page_count = page_count
        self.current_page = current_page
        self.input_values = input_values
        self.order = order
        self.ordering = ordering
        self.row_count = row_count
        self.first_item = page_count and ((current_page - 1) * limit + 1) or 0
        self.last_item = min(current_page * limit, row_count)
        self.reversed = False

        # Should reversed be true?
        for (field_name, ordering_values) in ordering.items():
            if ordering_values[0] == 0 and not ordering_values[1]:
                self.reversed = True

        # If ordering is empty, don't add it.
        input_values = {var_name + '_tgp_limit': limit}
        if ordering:
            input_values[var_name + '_tgp_ordering'] = ordering
        self.input_values.update(input_values)

        if current_page < page_count:
            self.input_values.update({
                var_name + '_tgp_no': current_page + 1,
                var_name + '_tgp_limit': limit
            })
            self.href_next = turbogears.url(
                cherrypy.request.path,
                self.input_values)
            self.input_values.update({
                var_name + '_tgp_no': 'last',
                var_name + '_tgp_limit': limit
            })
            self.href_last = turbogears.url(
                cherrypy.request.path,
                self.input_values)
        else:
            self.href_next = None
            self.href_last = None

        if current_page > 1:
            self.input_values.update({
                var_name + '_tgp_no': current_page - 1,
                var_name + '_tgp_limit': limit
            })
            self.href_prev = turbogears.url(
                cherrypy.request.path,
                self.input_values)
            self.input_values.update({
                var_name + '_tgp_no': 1,
                var_name + '_tgp_limit': limit
            })
            self.href_first = turbogears.url(
                cherrypy.request.path,
                self.input_values)
        else:
            self.href_prev = None
            self.href_first = None

    def get_href(self, page, order=None, reverse_order=None):
        # Note that reverse_order is not used.  It should be cleaned up here
        # and in the template.  I'm not removing it now because I don't want
        # to break the API.
        order = order or None
        input_values = self.input_values.copy()
        input_values[self.var_name + '_tgp_no'] = page
        if order:
            input_values[ self.var_name + '_tgp_order'] = order

        return turbogears.url('', input_values)

def _select_pages_to_show(current_page, page_count, max_pages):
    pages_to_show = []

    if max_pages < 3:
        msg = "The minimun value for max_pages on this algorithm is 3"
        raise StandardError(msg)

    if page_count <= max_pages:
        pages_to_show = range(1,page_count+1)

    pad = 0
    if not max_pages % 2:
        pad = 1

    start = current_page - (max_pages / 2) + pad
    end = current_page + (max_pages / 2)

    if start < 1:
        end = end + (start * -1) + 1
        start = 1

    if end > page_count:
        start = start - (end - page_count)
        end = page_count

    return range(start, end+1)

def sort_ordering(ordering, sort_name):
    """Rearrange ordering based on sort_name."""
    log.debug('sort called with %s and %s' % (ordering, sort_name))
    if ordering.setdefault(sort_name, [-1, True])[0] == 0:
        # Flip
        ordering[sort_name][1] = not ordering[sort_name][1]
    else:
        ordering[sort_name][0] = -1

    # re-sort dictionary
    items = ordering.items()
    items.sort(lambda x,y: cmp(x[1],y[1]))
    for i,v in enumerate(items):
        ordering[v[0]][0] = i

    log.debug('sort results is %s and %s' % (ordering, sort_name))

def sql_get_column(colname, var_data):
    """Return a column from var_data based on colname."""
    if SelectResults and isinstance(var_data, SelectResults):
        col = getattr(var_data.sourceClass.q, colname, None)

    elif SASelectResults and isinstance(var_data, SASelectResults):
        col = getattr(
                var_data._query.mapper.c,
                colname[len(var_data._query.mapper.column_prefix or ''):],
                None)

    elif Query and isinstance(var_data, Query):
        col = getattr(
                var_data.mapper.c,
                colname[len(var_data.mapper.column_prefix or ''):],
                None)
        #if no attribute is found, let's try searching for 'foreign' objects...
        # eg.: address.user.occupation.name
        if not col and colname.find('.'):
            seq = colname.split('.')
            mapper = var_data.mapper
            for propname in seq[:-1]:
                prop = mapper.get_property(
                    propname, resolve_synonyms=True, raiseerr=False)
                if not prop:
                    break
                mapper = prop.mapper
            # last item from split should be a simple attribute
            col = getattr(
                    mapper.c,
                    seq[-1][len(mapper.column_prefix or ''):],
                    None)
    else:
        raise StandardError, 'expected SelectResults'

    return col

def sql_order_col(col, ascending=True):
    """Return an ordered col for col."""
    if sqlalchemy and isinstance(col, sqlalchemy.sql.ColumnElement):
        if ascending:
            order_col = sqlalchemy.sql.asc(col)
        else:
            order_col = sqlalchemy.sql.desc(col)
    elif sqlobject and isinstance(col, types.InstanceType):
        # I don't like using InstanceType, but that's what sqlobject col type
        # is.
        if ascending:
            order_col = col
        else:
            order_col = sqlobject.DESC(col)
    else:
        raise StandardError, 'expected Column, but got %s' % str(type(col))
    return order_col

# Ordering re:
ordering_expr = re.compile(r"('\w+(\.\w+)*'): ?\[(\d+), ?(True|False)\]")

def convert_ordering(ordering):
    """Covert ordering unicode string to dict."""

    log.debug('ordering received %s' % str(ordering))

    # eval would be simple, but insecure.
    if not isinstance(ordering, (str, unicode)):
        raise ValueError, "ordering should be string or unicode."
    new_ordering = {}
    if ordering == u"{}":
        pass
    else:
        try:
            ordering_info_find = ordering_expr.findall(ordering)
            emsg = "Didn't match ordering for %s." % str(ordering)
            assert len(ordering_info_find) > 0, emsg
            for ordering_info in ordering_info_find:
                ordering_key = str(ordering_info[0]).strip("'")
                ordering_order = int(ordering_info[2])
                ordering_reverse = bool(ordering_info[3] == 'True')
                new_ordering[ordering_key] = [ordering_order,
                                              ordering_reverse]
        except StandardError, e:
            log.debug('FAILED to convert ordering.')
            new_ordering = {}
    log.debug('ordering converted to %s' % str(new_ordering))
    return new_ordering
TurboGears-1.0.4.2/turbogears/scheduler.py0000644000175000017500000004263710710636656017366 0ustar  faidefaide"""Module that provides a cron-like task scheduler.

This task scheduler is designed to be used from inside your own program.
You can schedule Python functions to be called at specific intervals or
days. It uses the standard 'sched' module for the actual task scheduling,
but provides much more:
    - repeated tasks (at intervals, or on specific days)
    - error handling (exceptions in tasks don't kill the scheduler)
    - optional to run scheduler in its own thread or separate process
    - optional to run a task in its own thread or separate process

If the threading module is available, you can use the various Threaded
variants of the scheduler and associated tasks. If threading is not
available, you could still use the forked variants. If fork is also
not available, all processing is done in a single process, sequentially.

There are three Scheduler classes:
    Scheduler    ThreadedScheduler    ForkedScheduler

You usually add new tasks to a scheduler using the add_interval_task or
add_daytime_task methods, with the appropriate processmethod argument
to select sequential, threaded or forked processing. NOTE: it is impossible
to add new tasks to a ForkedScheduler, after the scheduler has been started!
For more control you could use one of the following Task classes
and use schedule_task or schedule_task_abs:
    IntervalTask    ThreadedIntervalTask    ForkedIntervalTask
    WeekdayTask     ThreadedWeekdayTask     ForkedWeekdayTask
    MonthdayTask    ThreadedMonthdayTask    ForkedMonthdayTask

Kronos is the Greek God of Time.

This module is based on Kronos by Irmen de Jong, but has been modified
to better fit within TurboGears. Additionally, this module appeared to
no longer be supported/in development.
"""
#
#   $Id: kronos.py,v 1.5 2004/10/06 22:43:49 irmen Exp $
#
#   (c) Irmen de Jong.
#   This is open-source software, released under the MIT Software License:
#   http://www.opensource.org/licenses/mit-license.php
#


import os, sys
import sched, time
import traceback
import weakref

from turbogears.util import Enum

method = Enum("sequential", "forked", "threaded")

class Scheduler:
    """The Scheduler itself."""

    def __init__(self):
        self.running=True
        self.sched = sched.scheduler(time.time, self.__delayfunc)

    def __delayfunc(self, delay):
        # This delay function is basically a time.sleep() that is
        # divided up, so that we can check the self.running flag while delaying.
        # there is an additional check in here to ensure that the top item of
        # the queue hasn't changed
        if delay<10:
            time.sleep(delay)
        else:
            toptime = self.sched.queue[0][0]
            endtime = time.time() + delay
            period=5
            stoptime = endtime - period
            while self.running and stoptime > time.time() and \
                self.sched.queue[0][0] == toptime:
                time.sleep(period)
            if not self.running or self.sched.queue[0][0] != toptime:
                return
            now = time.time()
            if endtime > now:
                time.sleep(endtime - now)

    def _acquire_lock(self):    pass
    def _release_lock(self):    pass

    def add_interval_task(self, action, taskname, initialdelay, interval, processmethod, args, kw):
        """Add a new Interval Task to the schedule. A very short initialdelay or one of
        zero cannot be honored, you will see a slight delay before the task is first
        executed. This is because the scheduler needs to pick it up in its loop."""
        if initialdelay<0 or interval<1:
            raise ValueError("delay or interval must be >0")
        # Select the correct IntervalTask class. Not all types may be available!
        if processmethod==method.sequential:
            TaskClass=IntervalTask
        elif processmethod==method.threaded:
            TaskClass = ThreadedIntervalTask
        elif processmethod==method.forked:
            TaskClass = ForkedIntervalTask
        else:
            raise ValueError("invalid processmethod")
        if not args:
            args=[]
        if not kw:
            kw={}
        task = TaskClass(taskname, interval, action, args, kw)
        self.schedule_task(task, initialdelay)
        return task

    def add_daytime_task(self, action, taskname, weekdays, monthdays, timeonday, processmethod, args, kw):
        """Add a new Day Task (Weekday or Monthday) to the schedule."""
        if weekdays and monthdays:
            raise ValueError("you can only specify weekdays or monthdays, not both")
        if not args:
            args=[]
        if not kw:
            kw={}
        if weekdays:
            # Select the correct WeekdayTask class. Not all types may be available!
            if processmethod==method.sequential:
                TaskClass=WeekdayTask
            elif processmethod==method.threaded:
                TaskClass = ThreadedWeekdayTask
            elif processmethod==method.forked:
                TaskClass = ForkedWeekdayTask
            else:
                raise ValueError("invalid processmethod")
            task=TaskClass(taskname, weekdays, timeonday, action, args, kw)
        if monthdays:
            # Select the correct MonthdayTask class. Not all types may be available!
            if processmethod==method.sequential:
                TaskClass=MonthdayTask
            elif processmethod==method.threaded:
                TaskClass = ThreadedMonthdayTask
            elif processmethod==method.forked:
                TaskClass = ForkedMonthdayTask
            else:
                raise ValueError("invalid processmethod")
            task=TaskClass(taskname, monthdays, timeonday, action, args, kw)
        firsttime=task.get_schedule_time(True)
        self.schedule_task_abs(task, firsttime)
        return task

    def schedule_task(self, task, delay):
        """Low-level method to add a new task to the scheduler with the given delay (seconds)."""
        if self.running:
            self._acquire_lock()   # lock the sched queue, if needed
            try:
                task.event = self.sched.enter(delay, 0, task,
                            (weakref.ref(self),) )
            finally:
                self._release_lock()
        else:
            task.event = self.sched.enter(delay, 0, task,
                        (weakref.ref(self),) )

    def schedule_task_abs(self, task, abstime):
        """Low-level method to add a new task to the scheduler for the given absolute time value."""
        if self.running:
            self._acquire_lock()     # lock the sched queue, if needed
            try:
                task.event = self.sched.enterabs(abstime, 0, task,
                                    (weakref.ref(self),) )
            finally:
                self._release_lock()
        else:
            task.event = self.sched.enterabs(abstime, 0, task,
                                (weakref.ref(self),) )


    def start(self):
        """Start the scheduler."""
        self._run()

    def stop(self):
        """Remove all pending tasks and stop the Scheduler."""
        self.running=False
        self.sched.queue[:]=[]

    def cancel(self, task):
        self.sched.cancel(task.event)

    def _run(self):
        # Low-level run method to do the actual scheduling loop.
        while self.running:
            try:
                self.sched.run()
            except Exception,x:
                print >>sys.stderr, "ERROR DURING SCHEDULER EXECUTION",x
                print >>sys.stderr, "".join(traceback.format_exception(*sys.exc_info()))
                print >>sys.stderr, "-"*20
            # queue is empty; sleep a short while before checking again
            if self.running:
                time.sleep(5)


class Task:
    """Abstract base class of all scheduler tasks"""
    def __init__(self, name, action, args, kw):
        """This is an abstract class!"""
        self.name=name
        self.action=action
        self.args=args
        self.kw=kw

    def __call__(self, schedulerref):
        """Execute the task action in the scheduler's thread."""
        try:
            self.execute()
        except Exception,x:
            self.handle_exception(x)
        self.reschedule(schedulerref())

    def reschedule(self, scheduler):
        """This is an abstract class, this method is defined in one of the sub classes!"""
        raise NotImplementedError("you're using the abstract base class 'Task', use a concrete class instead")

    def execute(self):
        """Execute the actual task."""
        self.action(*self.args, **self.kw)

    def handle_exception(self, exc):
        """Handle any exception that occured during task execution."""
        print >>sys.stderr, "ERROR DURING TASK EXECUTION",exc
        print >>sys.stderr,"".join(traceback.format_exception(*sys.exc_info()))
        print >>sys.stderr,"-"*20


class IntervalTask(Task):
    """A repeated task that occurs at certain intervals (in seconds)."""
    def __init__(self, name, interval, action, args=None, kw=None):
        Task.__init__(self, name, action, args, kw)
        self.interval=interval

    def reschedule(self, scheduler):
        # reschedule this task according to its interval (in seconds).
        scheduler.schedule_task(self, self.interval)



class DayTaskRescheduler:
    """A mixin class that contains the reschedule logic for the DayTasks."""
    def __init__(self, timeonday):
        self.timeonday=timeonday

    def get_schedule_time(self, today):
        """Calculate the time value at which this task is to be scheduled."""
        now=list(time.localtime())
        if today:
            # schedule for today. let's see if that is still possible
            if (now[3], now[4]) >= self.timeonday:
                now[2]+=1  # too bad, it will be tomorrow
        else:
            now[2]+=1   # tomorrow
        now[3], now[4] = self.timeonday     # set new time on day (hour,minute)
        now[5]=0 # seconds
        return time.mktime(now)

    def reschedule(self, scheduler):
        # Reschedule this task according to the daytime for the task.
        # The task is scheduled for tomorrow, for the given daytime.
        # (The execute method in the concrete Task classes will check
        # if the current day is a day on which the task must run).
        abstime = self.get_schedule_time(False)
        scheduler.schedule_task_abs(self, abstime)


class WeekdayTask(DayTaskRescheduler, Task):
    """A task that is called at specific days in a week (1-7), at a fixed time on the day."""
    def __init__(self, name, weekdays, timeonday, action, args=None, kw=None):
        if type(timeonday) not in (list,tuple) or len(timeonday) != 2:
            raise TypeError("timeonday must be a 2-tuple (hour,minute)")
        if type(weekdays) not in (list,tuple):
            raise TypeError("weekdays must be a sequence of weekday numbers 1-7 (1 is Monday)")
        DayTaskRescheduler.__init__(self, timeonday)
        Task.__init__(self, name, action, args, kw)
        self.days=weekdays

    def execute(self):
        # This is called every day, at the correct time. We only need to
        # check if we should run this task today (this day of the week).
        weekday=time.localtime().tm_wday+1
        if weekday in self.days:
            self.action(*self.args, **self.kw)

class MonthdayTask(DayTaskRescheduler, Task):
    """A task that is called at specific days in a month (1-31), at a fixed time on the day."""
    def __init__(self, name, monthdays, timeonday, action, args=None, kw=None):
        if type(timeonday) not in (list,tuple) or len(timeonday) != 2:
            raise TypeError("timeonday must be a 2-tuple (hour,minute)")
        if type(monthdays) not in (list,tuple):
            raise TypeError("monthdays must be a sequence of monthdays numbers 1-31")
        DayTaskRescheduler.__init__(self, timeonday)
        Task.__init__(self, name, action, args, kw)
        self.days=monthdays

    def execute(self):
        # This is called every day, at the correct time. We only need to
        # check if we should run this task today (this day of the month).
        if time.localtime().tm_mday in self.days:
            self.action(*self.args, **self.kw)


try:
    import threading

    class ThreadedScheduler(Scheduler):
        """A Scheduler that runs in its own thread."""
        def __init__(self):
            Scheduler.__init__(self)
            self._lock=threading.Lock()     # we require a lock around the task queue
        def start(self):
            # Start method that splices of a thread in which the scheduler will run.
            self.thread=threading.Thread(target=self._run)
            self.thread.setDaemon(True)
            self.thread.start()
        def stop(self):
            # Stop method that stops the scheduler and waits for the thread to finish.
            Scheduler.stop(self)
            try:
                self.thread.join()
            except AttributeError:
                pass
        def _acquire_lock(self):
            self._lock.acquire()    # lock the thread's task queue
        def _release_lock(self):