diff options
author | Aaron Bentley <aaron.bentley@utoronto.ca> | 2007-07-15 12:43:30 -0400 |
---|---|---|
committer | Aaron Bentley <aaron.bentley@utoronto.ca> | 2007-07-15 12:43:30 -0400 |
commit | 7969f5ec12c0107b7c799a7bf26d8c43c1615b15 (patch) | |
tree | 1d7f3f22fceabe46752fe85907bd24c40175abe2 | |
parent | 9703aefca39996f954a91f5426f193c06661e69c (diff) | |
parent | b14962ab20d38c2a8fdaba8ecde55174141891a5 (diff) | |
download | bugseverywhere-7969f5ec12c0107b7c799a7bf26d8c43c1615b15.tar.gz |
Merge from panometrics
37 files changed, 888 insertions, 271 deletions
diff --git a/.be/bugs/9ce2f015-8ea0-43a5-a03d-fc36f6d202fe/values b/.be/bugs/9ce2f015-8ea0-43a5-a03d-fc36f6d202fe/values new file mode 100644 index 0000000..0292ab5 --- /dev/null +++ b/.be/bugs/9ce2f015-8ea0-43a5-a03d-fc36f6d202fe/values @@ -0,0 +1,35 @@ + + + +creator=abentley + + + + + + +severity=minor + + + + + + +status=open + + + + + + +summary=Add last-modified field to bugs + + + + + + +time=Thu, 14 Sep 2006 18:08:53 +0000 + + + diff --git a/.be/bugs/e2f6514c-5f9f-4734-a537-daf3fbe7e9a0/comments/bcd6e5d4-8d03-43ad-a10d-17619735d077/body b/.be/bugs/e2f6514c-5f9f-4734-a537-daf3fbe7e9a0/comments/bcd6e5d4-8d03-43ad-a10d-17619735d077/body new file mode 100644 index 0000000..c3b0f20 --- /dev/null +++ b/.be/bugs/e2f6514c-5f9f-4734-a537-daf3fbe7e9a0/comments/bcd6e5d4-8d03-43ad-a10d-17619735d077/body @@ -0,0 +1,5 @@ +There is definitely a difference between the person who reports a bug and the +person who enters it in the system. For example, you are reporting bugs to me, +and I am entering them in the Bugs Everywhere bug list. + +Perhaps there should be two fields. diff --git a/.be/bugs/e2f6514c-5f9f-4734-a537-daf3fbe7e9a0/comments/bcd6e5d4-8d03-43ad-a10d-17619735d077/values b/.be/bugs/e2f6514c-5f9f-4734-a537-daf3fbe7e9a0/comments/bcd6e5d4-8d03-43ad-a10d-17619735d077/values new file mode 100644 index 0000000..b2bedbb --- /dev/null +++ b/.be/bugs/e2f6514c-5f9f-4734-a537-daf3fbe7e9a0/comments/bcd6e5d4-8d03-43ad-a10d-17619735d077/values @@ -0,0 +1,28 @@ + + + +Content-type=text/plain + + + + + + +Date=Thu, 14 Sep 2006 18:05:48 +0000 + + + + + + +From=abentley + + + + + + +In-reply-to=e5decfc6-050b-4283-8776-977bf85b2c99 + + + diff --git a/.be/bugs/e2f6514c-5f9f-4734-a537-daf3fbe7e9a0/comments/e5decfc6-050b-4283-8776-977bf85b2c99/body b/.be/bugs/e2f6514c-5f9f-4734-a537-daf3fbe7e9a0/comments/e5decfc6-050b-4283-8776-977bf85b2c99/body new file mode 100644 index 0000000..23cb999 --- /dev/null +++ b/.be/bugs/e2f6514c-5f9f-4734-a537-daf3fbe7e9a0/comments/e5decfc6-050b-4283-8776-977bf85b2c99/body @@ -0,0 +1,3 @@ +Jens Mueller: +Referring to the fields describing a bug, I suggest the following: +The field 'Creator' should be named 'Reporter' (minor issue). diff --git a/.be/bugs/e2f6514c-5f9f-4734-a537-daf3fbe7e9a0/comments/e5decfc6-050b-4283-8776-977bf85b2c99/values b/.be/bugs/e2f6514c-5f9f-4734-a537-daf3fbe7e9a0/comments/e5decfc6-050b-4283-8776-977bf85b2c99/values new file mode 100644 index 0000000..9e82a6e --- /dev/null +++ b/.be/bugs/e2f6514c-5f9f-4734-a537-daf3fbe7e9a0/comments/e5decfc6-050b-4283-8776-977bf85b2c99/values @@ -0,0 +1,21 @@ + + + +Content-type=text/plain + + + + + + +Date=Thu, 14 Sep 2006 18:03:41 +0000 + + + + + + +From=abentley + + + diff --git a/.be/bugs/e2f6514c-5f9f-4734-a537-daf3fbe7e9a0/values b/.be/bugs/e2f6514c-5f9f-4734-a537-daf3fbe7e9a0/values new file mode 100644 index 0000000..ac013c5 --- /dev/null +++ b/.be/bugs/e2f6514c-5f9f-4734-a537-daf3fbe7e9a0/values @@ -0,0 +1,35 @@ + + + +creator=abentley + + + + + + +severity=minor + + + + + + +status=open + + + + + + +summary=Add a reporter field + + + + + + +time=Thu, 14 Sep 2006 16:47:57 +0000 + + + @@ -2,3 +2,9 @@ Bugs-Everywhere-Web/beweb/config.py ./build Bugs-Everywhere-Web/beweb/database.sqlite Bugs-Everywhere-Web/beweb/catwalk-session +*.pyc +*.sw[pon] +fte.dsk +*~ +./.shelf +Bugs-Everywhere-Web/devdata.sqlite diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/SOURCES.txt b/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/SOURCES.txt new file mode 100644 index 0000000..ab62ee4 --- /dev/null +++ b/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/SOURCES.txt @@ -0,0 +1,44 @@ +README.txt +setup.py +start-beweb.py +Bugs_Everywhere_Web.egg-info/PKG-INFO +Bugs_Everywhere_Web.egg-info/SOURCES.txt +Bugs_Everywhere_Web.egg-info/dependency_links.txt +Bugs_Everywhere_Web.egg-info/not-zip-safe +Bugs_Everywhere_Web.egg-info/paster_plugins.txt +Bugs_Everywhere_Web.egg-info/requires.txt +Bugs_Everywhere_Web.egg-info/sqlobject.txt +Bugs_Everywhere_Web.egg-info/top_level.txt +Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/SOURCES.txt +Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/not-zip-safe +Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/requires.txt +Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/sqlobject.txt +Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/top_level.txt +beweb/__init__.py +beweb/config.py +beweb/controllers.py +beweb/formatting.py +beweb/json.py +beweb/model.py +beweb/prest.py +beweb/release.py +beweb/config/__init__.py +beweb/templates/__init__.py +beweb/tests/__init__.py +beweb/tests/test_controllers.py +beweb/tests/test_model.py +libbe/__init__.py +libbe/arch.py +libbe/bugdir.py +libbe/bzr.py +libbe/cmdutil.py +libbe/config.py +libbe/diff.py +libbe/mapfile.py +libbe/names.py +libbe/no_rcs.py +libbe/plugin.py +libbe/rcs.py +libbe/restconvert.py +libbe/tests.py +libbe/utility.py diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/dependency_links.txt b/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/not-zip-safe b/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/not-zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/requires.txt b/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/requires.txt index b4e1d25..5fd6f71 100644 --- a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/requires.txt +++ b/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/requires.txt @@ -1 +1 @@ -TurboGears >= 0.8a4
\ No newline at end of file +TurboGears >= 1.0b1
\ No newline at end of file diff --git a/Bugs-Everywhere-Web/beweb/config/app.cfg b/Bugs-Everywhere-Web/beweb/config/app.cfg new file mode 100644 index 0000000..d37cf67 --- /dev/null +++ b/Bugs-Everywhere-Web/beweb/config/app.cfg @@ -0,0 +1,42 @@ +[global] +# The settings in this file should not vary depending on the deployment +# environment. dev.cfg and prod.cfg are the locations for +# the different deployment settings. Settings in this file will +# be overridden by settings in those other files. + +# The commented out values below are the defaults + +# VIEW + +# which view (template engine) to use if one is not specified in the +# template name +# tg.defaultview = "kid" + +# The following kid settings determine the settings used by the kid serializer. + +# One of (html|xml|json) +# kid.outputformat="html" + +# kid.encoding="utf-8" + +# The sitetemplate is used for overall styling of a site that +# includes multiple TurboGears applications +# tg.sitetemplate="<packagename.templates.templatename>" + +# Allow every exposed function to be called as json, +# tg.allow_json = False + +# List of Widgets to include on every page. +# for exemple ['turbogears.mochikit'] +# tg.include_widgets = [] + +# Set to True if the scheduler should be started +# tg.scheduler = False + +[/static] +static_filter.on = True +static_filter.dir = "%(top_level_dir)s/static" + +[/favicon.ico] +static_filter.on = True +static_filter.file = "%(top_level_dir)s/static/images/favicon.ico" diff --git a/Bugs-Everywhere-Web/beweb/config/log.cfg b/Bugs-Everywhere-Web/beweb/config/log.cfg new file mode 100644 index 0000000..ce776f8 --- /dev/null +++ b/Bugs-Everywhere-Web/beweb/config/log.cfg @@ -0,0 +1,29 @@ +# LOGGING +# Logging is often deployment specific, but some handlers and +# formatters can be defined here. + +[logging] +[[formatters]] +[[[message_only]]] +format='*(message)s' + +[[[full_content]]] +format='*(asctime)s *(name)s *(levelname)s *(message)s' + +[[handlers]] +[[[debug_out]]] +class='StreamHandler' +level='DEBUG' +args='(sys.stdout,)' +formatter='full_content' + +[[[access_out]]] +class='StreamHandler' +level='INFO' +args='(sys.stdout,)' +formatter='message_only' + +[[[error_out]]] +class='StreamHandler' +level='ERROR' +args='(sys.stdout,)' diff --git a/Bugs-Everywhere-Web/beweb/controllers.py b/Bugs-Everywhere-Web/beweb/controllers.py index 4417a63..358e74a 100644 --- a/Bugs-Everywhere-Web/beweb/controllers.py +++ b/Bugs-Everywhere-Web/beweb/controllers.py @@ -1,12 +1,20 @@ -import turbogears -from turbogears import controllers, expose, redirect, identity +import logging + import cherrypy +import turbogears +from turbogears import controllers, expose, validate, redirect, identity + from libbe.bugdir import (tree_root, cmp_severity, new_bug, new_comment, NoRootEntry) from libbe import names from config import projects from prest import PrestHandler, provide_action + +from beweb import json + +log = logging.getLogger("beweb.controllers") + def project_tree(project): try: return tree_root(projects[project][1]) @@ -112,9 +120,9 @@ class Bug(PrestHandler): assigned = None bug.assigned = assigned bug.save() - bug.rcs.precommit(bug.path) - bug.rcs.commit(bug.path, "Auto-commit") - bug.rcs.postcommit(bug.path) +# bug.rcs.precommit(bug.path) +# bug.rcs.commit(bug.path, "Auto-commit") +# bug.rcs.postcommit(bug.path) raise cherrypy.HTTPRedirect(bug_list_url(bug_data["project"])) def instantiate(self, project, bug): diff --git a/Bugs-Everywhere-Web/beweb/formatting.py b/Bugs-Everywhere-Web/beweb/formatting.py index 44ed849..b68d328 100644 --- a/Bugs-Everywhere-Web/beweb/formatting.py +++ b/Bugs-Everywhere-Web/beweb/formatting.py @@ -33,7 +33,7 @@ def soft_text(text): def soft_pre(text): return XML('<div style="font-family: monospace">'+ - ''.join(soft_text(text))+'</div>') + ''.join(soft_text(text)).encode('utf-8')+'</div>') def get_rest_body(rest): diff --git a/Bugs-Everywhere-Web/beweb/json.py b/Bugs-Everywhere-Web/beweb/json.py new file mode 100644 index 0000000..6e100c3 --- /dev/null +++ b/Bugs-Everywhere-Web/beweb/json.py @@ -0,0 +1,13 @@ +# This module provides helper functions for the JSON part of your +# view, if you are providing a JSON-based API for your app. + +# Here's what most rules would look like: +# @jsonify.when("isinstance(obj, YourClass)") +# def jsonify_yourclass(obj): +# return [obj.val1, obj.val2] +# +# The goal is to break your objects down into simple values: +# lists, dicts, numbers and strings + +from turbojson.jsonify import jsonify + diff --git a/Bugs-Everywhere-Web/beweb/model.py b/Bugs-Everywhere-Web/beweb/model.py index 6a603bb..aa4b6b6 100644 --- a/Bugs-Everywhere-Web/beweb/model.py +++ b/Bugs-Everywhere-Web/beweb/model.py @@ -1,15 +1,107 @@ +from datetime import datetime + from sqlobject import * from turbogears.database import PackageHub -# Uncomment the following line if you wish to use Identity and SO_Provider -from turbogears.identity.soprovider import TG_User, TG_Group, TG_Permission from turbogears import identity hub = PackageHub("beweb") __connection__ = hub -def people_map(): - return dict([(u.userId, u.displayName) for u in TG_User.select() if - "fixbugs" in identity.current.permissions]) +class Visit(SQLObject): + class sqlmeta: + table = "visit" + + visit_key = StringCol(length=40, alternateID=True, + alternateMethodName="by_visit_key") + created = DateTimeCol(default=datetime.now) + expiry = DateTimeCol() + + def lookup_visit(cls, visit_key): + try: + return cls.by_visit_key(visit_key) + except SQLObjectNotFound: + return None + lookup_visit = classmethod(lookup_visit) + +class VisitIdentity(SQLObject): + visit_key = StringCol(length=40, alternateID=True, + alternateMethodName="by_visit_key") + user_id = IntCol() + + +class Group(SQLObject): + """ + An ultra-simple group definition. + """ + + # names like "Group", "Order" and "User" are reserved words in SQL + # so we set the name to something safe for SQL + class sqlmeta: + table = "tg_group" + + group_name = UnicodeCol(length=16, alternateID=True, + alternateMethodName="by_group_name") + display_name = UnicodeCol(length=255) + created = DateTimeCol(default=datetime.now) + + # collection of all users belonging to this group + users = RelatedJoin("User", intermediateTable="user_group", + joinColumn="group_id", otherColumn="user_id") + + # collection of all permissions for this group + permissions = RelatedJoin("Permission", joinColumn="group_id", + intermediateTable="group_permission", + otherColumn="permission_id") + -# class YourDataClass(SQLObject): -# pass +class User(SQLObject): + """ + Reasonably basic User definition. Probably would want additional attributes. + """ + # names like "Group", "Order" and "User" are reserved words in SQL + # so we set the name to something safe for SQL + class sqlmeta: + table = "tg_user" + + child_name = UnicodeCol(length=255) + user_name = UnicodeCol(length=16, alternateID=True, + alternateMethodName="by_user_name") + email_address = UnicodeCol(length=255, alternateID=True, + alternateMethodName="by_email_address") + display_name = UnicodeCol(length=255) + password = UnicodeCol(length=40) + created = DateTimeCol(default=datetime.now) + + # groups this user belongs to + groups = RelatedJoin("Group", intermediateTable="user_group", + joinColumn="user_id", otherColumn="group_id") + + def _get_permissions(self): + perms = set() + for g in self.groups: + perms = perms | set(g.permissions) + return perms + + def _set_password(self, cleartext_password): + "Runs cleartext_password through the hash algorithm before saving." + hash = identity.encrypt_password(cleartext_password) + self._SO_set_password(hash) + + def set_password_raw(self, password): + "Saves the password as-is to the database." + self._SO_set_password(password) + + + +class Permission(SQLObject): + permission_name = UnicodeCol(length=16, alternateID=True, + alternateMethodName="by_permission_name") + description = UnicodeCol(length=255) + + groups = RelatedJoin("Group", + intermediateTable="group_permission", + joinColumn="permission_id", + otherColumn="group_id") + +def people_map(): + return dict((u.user_name, u.display_name) for u in User.select()) diff --git a/Bugs-Everywhere-Web/beweb/release.py b/Bugs-Everywhere-Web/beweb/release.py index 0232912..9d64bf7 100644 --- a/Bugs-Everywhere-Web/beweb/release.py +++ b/Bugs-Everywhere-Web/beweb/release.py @@ -3,6 +3,7 @@ version = "1.0" # description = "Your plan to rule the world" +# long_description = "More description about your plan" # author = "Your Name Here" # email = "YourEmail@YourDomain" # copyright = "Vintage 2006 - a good year indeed" diff --git a/Bugs-Everywhere-Web/beweb/static/css/style.css b/Bugs-Everywhere-Web/beweb/static/css/style.css index ada46f3..6fe197f 100644 --- a/Bugs-Everywhere-Web/beweb/static/css/style.css +++ b/Bugs-Everywhere-Web/beweb/static/css/style.css @@ -1,116 +1,116 @@ -table -{ - background-color: black; -} -td -{ - background-color: white; -} -h1 -{ - font-family: "Verdana"; - font-weight: bold; - font-size: 120%; - margin-bottom:0; - color: #990; -} - -tr.closed td -{ - background-color: #ccc; -} -tr.closedeven td -{ - background-color: #ccc; -} -tr.closedodd td -{ - background-color: #dda; -} - -a:visited, a:link -{ - color: #990; - text-decoration: None; -} -td a:visited, td a:link -{ - display: block; -} -a:visited:hover, a:link:hover -{ - text-decoration: underline; -} -td a:visited:hover, td a:link:hover -{ - color:black; - background-color:#dda; - text-decoration: None; - display: block; -} - -body -{ - font-family: "Verdana"; - font-size:11pt; - background-color: white; -} -.comment -{ -} -.comment table -{ - background-color: transparent; -} -.comment td -{ - background-color: transparent; -} -.comment pre -{ - font-family: "Verdana"; -} -#header -{ - color: black; - font-weight: bold; - background-image: url(/static/images/half-spiral.png); - background-position: right center; - background-repeat: no-repeat; - background-color: #ff0; -} -#header ul.navoption -{ - display: block; - float: right; - margin: 0; - padding-right: 30px; -} -#header li -{ - display: inline; - margin:0; - padding:0; -} -table.insetbox -{ - margin-top: 0.5em; - margin-bottom: 0.5em; -} -.insetbox tr, .insetbox td -{ - margin: 0; - padding: 0; -} -pre.traceback -{ - font-family: Verdana, Ariel, Helvetica, sanserif; -} -tr.even td -{ - background-color: #eee; -} -tr.odd td -{ - background-color: #ffe; -} +table
+{
+ background-color: black;
+}
+td
+{
+ background-color: white;
+}
+h1
+{
+ font-family: "Verdana";
+ font-weight: bold;
+ font-size: 120%;
+ margin-bottom:0;
+ color: #990;
+}
+
+tr.closed td
+{
+ background-color: #ccc;
+}
+tr.closedeven td
+{
+ background-color: #ccc;
+}
+tr.closedodd td
+{
+ background-color: #dda;
+}
+
+a:visited, a:link
+{
+ color: #990;
+ text-decoration: None;
+}
+td a:visited, td a:link
+{
+ display: block;
+}
+a:visited:hover, a:link:hover
+{
+ text-decoration: underline;
+}
+td a:visited:hover, td a:link:hover
+{
+ color:black;
+ background-color:#dda;
+ text-decoration: None;
+ display: block;
+}
+
+body
+{
+ font-family: "Verdana";
+ font-size:11pt;
+ background-color: white;
+}
+.comment
+{
+}
+.comment table
+{
+ background-color: transparent;
+}
+.comment td
+{
+ background-color: transparent;
+}
+.comment pre
+{
+ font-family: "Verdana";
+}
+#header
+{
+ color: black;
+ font-weight: bold;
+ background-image: url(/static/images/half-spiral.png);
+ background-position: right center;
+ background-repeat: no-repeat;
+ background-color: #ff0;
+}
+#header ul.navoption
+{
+ display: block;
+ float: right;
+ margin: 0;
+ padding-right: 30px;
+}
+#header li
+{
+ display: inline;
+ margin:0;
+ padding:0;
+}
+table.insetbox
+{
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
+.insetbox tr, .insetbox td
+{
+ margin: 0;
+ padding: 0;
+}
+pre.traceback
+{
+ font-family: Verdana, Ariel, Helvetica, sanserif;
+}
+tr.even td
+{
+ background-color: #eee;
+}
+tr.odd td
+{
+ background-color: #ffe;
+}
diff --git a/Bugs-Everywhere-Web/beweb/static/images/header_inner.png b/Bugs-Everywhere-Web/beweb/static/images/header_inner.png Binary files differnew file mode 100644 index 0000000..2b2d87d --- /dev/null +++ b/Bugs-Everywhere-Web/beweb/static/images/header_inner.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/info.png b/Bugs-Everywhere-Web/beweb/static/images/info.png Binary files differnew file mode 100644 index 0000000..329c523 --- /dev/null +++ b/Bugs-Everywhere-Web/beweb/static/images/info.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/ok.png b/Bugs-Everywhere-Web/beweb/static/images/ok.png Binary files differnew file mode 100644 index 0000000..fee6751 --- /dev/null +++ b/Bugs-Everywhere-Web/beweb/static/images/ok.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/tg_under_the_hood.png b/Bugs-Everywhere-Web/beweb/static/images/tg_under_the_hood.png Binary files differnew file mode 100644 index 0000000..bc9c79c --- /dev/null +++ b/Bugs-Everywhere-Web/beweb/static/images/tg_under_the_hood.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/under_the_hood_blue.png b/Bugs-Everywhere-Web/beweb/static/images/under_the_hood_blue.png Binary files differnew file mode 100644 index 0000000..90e84b7 --- /dev/null +++ b/Bugs-Everywhere-Web/beweb/static/images/under_the_hood_blue.png diff --git a/Bugs-Everywhere-Web/beweb/templates/login.kid b/Bugs-Everywhere-Web/beweb/templates/login.kid index 2c150db..e7ad852 100644 --- a/Bugs-Everywhere-Web/beweb/templates/login.kid +++ b/Bugs-Everywhere-Web/beweb/templates/login.kid @@ -8,7 +8,7 @@ <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/> <title>Login</title> - <style> + <style type="text/css"> #loginBox { width: 30%; @@ -97,7 +97,7 @@ </tr> <tr> <td colspan="2" class="buttons"> - <input type="submit" value="Login"/> + <input type="submit" name="login" value="Login"/> </td> </tr> </table> diff --git a/Bugs-Everywhere-Web/beweb/templates/master.kid b/Bugs-Everywhere-Web/beweb/templates/master.kid index 54f6bad..0772524 100644 --- a/Bugs-Everywhere-Web/beweb/templates/master.kid +++ b/Bugs-Everywhere-Web/beweb/templates/master.kid @@ -1,71 +1,71 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<?python import sitetemplate ?> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="sitetemplate"> - -<head py:match="item.tag=='{http://www.w3.org/1999/xhtml}head'" py:attrs="item.items()"> - <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/> - <title py:if="False">Your title goes here</title> - <link rel="stylesheet" type="text/css" href="/static/css/style.css"/> - <meta py:replace="item[:]"/> - <style> - #pageLogin - { - font-size: 10px; - font-family: verdana; - text-align: right; - } - </style> -</head> - -<body py:match="item.tag=='{http://www.w3.org/1999/xhtml}body'" py:attrs="item.items()"> -<div id="header"><div style="float: left">b u g s e v r y w h e r e</div><ul class="navoption"><li><a href="/about/">About</a></li></ul> </div> - <div py:if="tg.config('identity.on',False) and not 'logging_in' in locals()" - id="pageLogin"> - <span py:if="tg.identity.anonymous"> - <a href="/login">Login</a> - </span> - <span py:if="not tg.identity.anonymous"> - Welcome ${tg.identity.user.displayName}. - <a href="/logout">Logout</a> - </span> - </div> - - <div py:if="tg_flash" class="flash" py:content="tg_flash"></div> - - <div py:replace="item[:]"/> - -</body> -<table py:match="item.tag=='{http://www.w3.org/1999/xhtml}insetbox'" cellspacing="0" cellpadding="0" border="0" class="insetbox"> -<tr height="19"><td background="/static/images/is-tl.png" width="19"/> - <td background="/static/images/is-t.png" /> - <td background="/static/images/is-tr.png" width="11"></td> -</tr> -<tr> - <td background="/static/images/is-l.png"/> - <td py:content="item[:]"> Hello, this is some random text</td> - <td background="/static/images/is-r.png"/> -</tr> -<tr height="11"> - <td background="/static/images/is-bl.png"/> - <td background="/static/images/is-b.png" /> - <td background="/static/images/is-br.png"/> -</tr> -</table> -<table py:match="item.tag=='{http://www.w3.org/1999/xhtml}dsbox'" cellspacing="0" cellpadding="0" border="0" class="dsbox"> -<tr height="11"><td background="/static/images/ds-tl.png" width="11"/> - <td background="/static/images/ds-t.png" /> - <td background="/static/images/ds-tr.png" width="19"></td> -</tr> -<tr> - <td background="/static/images/ds-l.png"/> - <td py:content="item[:]"> Hello, this is some random text</td> - <td background="/static/images/ds2-r.png"/> -</tr> -<tr height="19"> - <td background="/static/images/ds-bl.png"/> - <td background="/static/images/ds2-b.png" /> - <td background="/static/images/ds-br.png"/> -</tr> -</table> - -</html> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<?python import sitetemplate ?>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="sitetemplate">
+
+<head py:match="item.tag=='{http://www.w3.org/1999/xhtml}head'" py:attrs="item.items()">
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+ <title py:if="False">Your title goes here</title>
+ <link rel="stylesheet" type="text/css" href="/static/css/style.css"/>
+ <meta py:replace="item[:]"/>
+ <style type="text/css">
+ #pageLogin
+ {
+ font-size: 10px;
+ font-family: verdana;
+ text-align: right;
+ }
+ </style>
+</head>
+
+<body py:match="item.tag=='{http://www.w3.org/1999/xhtml}body'" py:attrs="item.items()">
+<div id="header"><div style="float: left">b u g s e v r y w h e r e</div><ul class="navoption"><li><a href="/about/">About</a></li></ul> </div>
+ <div py:if="tg.config('identity.on',False) and not 'logging_in' in locals()"
+ id="pageLogin">
+ <span py:if="tg.identity.anonymous">
+ <a href="/login">Login</a>
+ </span>
+ <span py:if="not tg.identity.anonymous">
+ Welcome ${tg.identity.user.display_name}.
+ <a href="/logout">Logout</a>
+ </span>
+ </div>
+
+ <div py:if="tg_flash" class="flash" py:content="tg_flash"></div>
+
+ <div py:replace="[item.text]+item[:]"/>
+
+<table py:match="item.tag=='{http://www.w3.org/1999/xhtml}insetbox'" cellspacing="0" cellpadding="0" border="0" class="insetbox">
+<tr height="19"><td background="/static/images/is-tl.png" width="19"/>
+ <td background="/static/images/is-t.png" />
+ <td background="/static/images/is-tr.png" width="11"></td>
+</tr>
+<tr>
+ <td background="/static/images/is-l.png"/>
+ <td py:content="item[:]"> Hello, this is some random text</td>
+ <td background="/static/images/is-r.png"/>
+</tr>
+<tr height="11">
+ <td background="/static/images/is-bl.png"/>
+ <td background="/static/images/is-b.png" />
+ <td background="/static/images/is-br.png"/>
+</tr>
+</table>
+<table py:match="item.tag=='{http://www.w3.org/1999/xhtml}dsbox'" cellspacing="0" cellpadding="0" border="0" class="dsbox">
+<tr height="11"><td background="/static/images/ds-tl.png" width="11"/>
+ <td background="/static/images/ds-t.png" />
+ <td background="/static/images/ds-tr.png" width="19"></td>
+</tr>
+<tr>
+ <td background="/static/images/ds-l.png"/>
+ <td py:content="item[:]"> Hello, this is some random text</td>
+ <td background="/static/images/ds2-r.png"/>
+</tr>
+<tr height="19">
+ <td background="/static/images/ds-bl.png"/>
+ <td background="/static/images/ds2-b.png" />
+ <td background="/static/images/ds-br.png"/>
+</tr>
+</table>
+</body>
+
+</html>
diff --git a/Bugs-Everywhere-Web/beweb/templates/welcome.kid b/Bugs-Everywhere-Web/beweb/templates/welcome.kid index 0d3cf3e..08abd21 100644 --- a/Bugs-Everywhere-Web/beweb/templates/welcome.kid +++ b/Bugs-Everywhere-Web/beweb/templates/welcome.kid @@ -1,33 +1,50 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" - py:extends="'master.kid'"> - -<head> - <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/> - <title>Welcome to TurboGears</title> -</head> - -<body> - <p>Congratulations, your TurboGears application is running as of <span py:replace="now">now</span>.</p> - - <h2>Are you ready to Gear Up?</h2> - - <p>Take the following steps to dive right in:</p> - - <ol> - <li>Edit your project's model.py to create SQLObjects representing the data you're working with</li> - <li>Edit your dev.cfg file to point to the database you'll be using</li> - <li>Run "<code>tg-admin sql create</code>" to create the tables in the database</li> - <li>Edit controllers.py to add the functionality to your webapp</li> - <li>Change the master.kid template to have the headers and footers for your application.</li> - <li>Change welcome.kid (this template) or create a new one to display your data</li> - <li>Repeat steps 4-6 until done.</li> - <li><b>Profit!</b></li> - </ol> - - <p>If you haven't already, you might check out some of the <a href="http://www.turbogears.org/docs/" >documentation</a>.</p> - - <p>Thanks for using TurboGears! See you on the <a href="http://groups.google.com/group/turbogears" >mailing list</a> and the "turbogears" channel on irc.freenode.org!</p> - -</body> -</html> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'master.kid'">
+<head>
+<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
+<title>Welcome to TurboGears</title>
+</head>
+<body>
+<div id="header"> </div>
+<div id="main_content">
+ <div id="status_block">Your TurboGears application is now running.</div>
+ <!--h1>Take steps to dive right in:</h1-->
+ <div id="sidebar">
+ <h2>Learn more</h2>
+ Learn more about TurboGears and take part in its
+ development
+ <ul class="links">
+ <li><a href="http://www.turbogears.org">Official website</a></li>
+ <li><a href="http://docs.turbogears.org">Documentation</a></li>
+ <li><a href="http://trac.turbogears.org/turbogears/">Trac
+ (bugs/suggestions)</a></li>
+ <li><a href="http://groups.google.com/group/turbogears"> Mailing list</a> </li>
+ </ul>
+ </div>
+ <div id="getting_started">
+ <ol id="getting_started_steps">
+ <li class="getting_started">
+ <h3>Model</h3>
+ <p> <a href="http://docs.turbogears.org/1.0/GettingStarted/DefineDatabase">Design models</a> in the <span class="code">model.py</span>.<br/>
+ Edit <span class="code">dev.cfg</span> to <a href="http://docs.turbogears.org/1.0/GettingStarted/UseDatabase">use a different backend</a>, or start with a pre-configured SQLite database. <br/>
+ Use script <span class="code">tg-admin sql create</span> to create the database tables.</p>
+ </li>
+ <li class="getting_started">
+ <h3>View</h3>
+ <p> Edit <a href="http://docs.turbogears.org/1.0/GettingStarted/Kid">html-like templates</a> in the <span class="code">/templates</span> folder;<br/>
+ Put all <a href="http://docs.turbogears.org/1.0/StaticFiles">static contents</a> in the <span class="code">/static</span> folder. </p>
+ </li>
+ <li class="getting_started">
+ <h3>Controller</h3>
+ <p> Edit <span class="code"> controllers.py</span> and <a href="http://docs.turbogears.org/1.0/GettingStarted/CherryPy">build your
+ website structure</a> with the simplicity of Python objects. <br/>
+ TurboGears will automatically reload itself when you modify your project. </p>
+ </li>
+ </ol>
+ <div class="notice"> If you create something cool, please <a href="http://groups.google.com/group/turbogears">let people know</a>, and consider contributing something back to the <a href="http://groups.google.com/group/turbogears">community</a>.</div>
+ </div>
+ <!-- End of getting_started -->
+</div>
+</body>
+</html>
diff --git a/Bugs-Everywhere-Web/beweb/tests/test_model.py b/Bugs-Everywhere-Web/beweb/tests/test_model.py index 5346f8b..74c4e83 100644 --- a/Bugs-Everywhere-Web/beweb/tests/test_model.py +++ b/Bugs-Everywhere-Web/beweb/tests/test_model.py @@ -4,21 +4,20 @@ # choice for testing, because you can use an in-memory database # which is very fast. -from turbogears import testutil -#from beweb.model import YourDataClass -#from turbogears.identity.soprovider import TG_User +from turbogears import testutil, database +# from beweb.model import YourDataClass, User # database.set_db_uri("sqlite:///:memory:") -# class testTG_User(testutil.DBTest): +# class TestUser(testutil.DBTest): # def get_model(self): -# return TG_User +# return User # # def test_creation(self): # "Object creation should set the name" -# obj = TG_User(userId = "creosote", -# emailAddress = "spam@python.not", -# displayName = "Mr Creosote", +# obj = User(user_name = "creosote", +# email_address = "spam@python.not", +# display_name = "Mr Creosote", # password = "Wafer-thin Mint") -# assert obj.displayName == "Mr Creosote" +# assert obj.display_name == "Mr Creosote" diff --git a/Bugs-Everywhere-Web/dev.cfg b/Bugs-Everywhere-Web/dev.cfg index c8e1658..eda9e6c 100644 --- a/Bugs-Everywhere-Web/dev.cfg +++ b/Bugs-Everywhere-Web/dev.cfg @@ -2,14 +2,19 @@ # This is where all of your settings go for your development environment # Settings that are the same for both development and production # (such as template engine, encodings, etc.) all go in -# yourpackage/config/app.cfg +# beweb/config/app.cfg # DATABASE # pick the form for your database # sqlobject.dburi="postgres://username@hostname/databasename" # sqlobject.dburi="mysql://username:password@hostname:port/databasename" -sqlobject.dburi="sqlite://%(package_dir)s/database.sqlite" +# sqlobject.dburi="sqlite://%(package_dir)s/database.sqlite" + +# If you have sqlite, here's a simple default to get you started +# in development +sqlobject.dburi="sqlite://%(current_dir_uri)s/devdata.sqlite" + # if you are using a database or table type without transactions # (MySQL default, for example), you should turn off transactions @@ -17,8 +22,7 @@ sqlobject.dburi="sqlite://%(package_dir)s/database.sqlite" # sqlobject.dburi="notrans_mysql://username:password@hostname:port/databasename" # for Windows users, sqlite URIs look like: -# sqlobject.dburi="sqlite:///drive_letter|/path/to/file" - +# sqlobject.dburi="sqlite:///drive_letter:/path/to/file" # SERVER @@ -31,7 +35,37 @@ sqlobject.dburi="sqlite://%(package_dir)s/database.sqlite" server.environment="development" autoreload.package="beweb" +# session_filter.on = True + # Set to True if you'd like to abort execution if a controller gets an # unexpected parameter. False by default tg.strict_parameters = True +identity.on = True +visit.on = True +identity.soprovider.model.user="beweb.model.User" +identity.soprovider.model.group="beweb.model.Group" +identity.soprovider.model.permission="beweb.model.Permission" + + +# LOGGING +# Logging configuration generally follows the style of the standard +# Python logging module configuration. Note that when specifying +# log format messages, you need to use *() for formatting variables. +# Deployment independent log configuration is in beweb/config/log.cfg +[logging] + +[[loggers]] +[[[beweb]]] +level='DEBUG' +qualname='beweb' +handlers=['debug_out'] + +[[[allinfo]]] +level='INFO' +handlers=['debug_out'] +[[[access]]] +level='INFO' +qualname='turbogears.access' +handlers=['access_out'] +propagate=0 diff --git a/Bugs-Everywhere-Web/sample-prod.cfg b/Bugs-Everywhere-Web/sample-prod.cfg new file mode 100644 index 0000000..d1052f8 --- /dev/null +++ b/Bugs-Everywhere-Web/sample-prod.cfg @@ -0,0 +1,71 @@ +[global] +# This is where all of your settings go for your production environment. +# You'll copy this file over to your production server and provide it +# as a command-line option to your start script. +# Settings that are the same for both development and production +# (such as template engine, encodings, etc.) all go in +# beweb/config/app.cfg + +# pick the form for your database +# sqlobject.dburi="postgres://username@hostname/databasename" +# sqlobject.dburi="mysql://username:password@hostname:port/databasename" +# sqlobject.dburi="sqlite:///file_name_and_path" + +# If you have sqlite, here's a simple default to get you started +# in development +sqlobject.dburi="sqlite://%(current_dir_uri)s/devdata.sqlite" + + +# if you are using a database or table type without transactions +# (MySQL default, for example), you should turn off transactions +# by prepending notrans_ on the uri +# sqlobject.dburi="notrans_mysql://username:password@hostname:port/databasename" + +# for Windows users, sqlite URIs look like: +# sqlobject.dburi="sqlite:///drive_letter:/path/to/file" + + +# SERVER + +server.environment="production" + +# Sets the number of threads the server uses +# server.thread_pool = 1 + +# if this is part of a larger site, you can set the path +# to the TurboGears instance here +# server.webpath="" + +# session_filter.on = True + +# Set to True if you'd like to abort execution if a controller gets an +# unexpected parameter. False by default +# tg.strict_parameters = False + +# LOGGING +# Logging configuration generally follows the style of the standard +# Python logging module configuration. Note that when specifying +# log format messages, you need to use *() for formatting variables. +# Deployment independent log configuration is in beweb/config/log.cfg +[logging] + +[[handlers]] + +[[[access_out]]] +# set the filename as the first argument below +args="('server.log',)" +class='FileHandler' +level='INFO' +formatter='message_only' + +[[loggers]] +[[[beweb]]] +level='ERROR' +qualname='beweb' +handlers=['error_out'] + +[[[access]]] +level='INFO' +qualname='turbogears.access' +handlers=['access_out'] +propagate=0 diff --git a/Bugs-Everywhere-Web/setup.py b/Bugs-Everywhere-Web/setup.py index 29f1d6f..8ba3da2 100644 --- a/Bugs-Everywhere-Web/setup.py +++ b/Bugs-Everywhere-Web/setup.py @@ -16,7 +16,9 @@ setup( #download_url=download_url, #license=license, - install_requires = ["TurboGears >= 0.9a4"], + install_requires = [ + "TurboGears >= 1.0b1", + ], scripts = ["start-beweb.py"], zip_safe=False, packages=find_packages(), diff --git a/Bugs-Everywhere-Web/start-beweb.py b/Bugs-Everywhere-Web/start-beweb.py index 6eba527..39100e7 100755 --- a/Bugs-Everywhere-Web/start-beweb.py +++ b/Bugs-Everywhere-Web/start-beweb.py @@ -15,15 +15,14 @@ import sys # probably installed if len(sys.argv) > 1: turbogears.update_config(configfile=sys.argv[1], - modulename="beweb.config.app") + modulename="beweb.config") elif exists(join(dirname(__file__), "setup.py")): turbogears.update_config(configfile="dev.cfg", - modulename="beweb.config.app") + modulename="beweb.config") else: turbogears.update_config(configfile="prod.cfg", - modulename="beweb.config.app") + modulename="beweb.config") from beweb.controllers import Root -cherrypy.root = Root() -cherrypy.server.start() +turbogears.start_server(Root()) diff --git a/libbe/bugdir.py b/libbe/bugdir.py index 414b47e..bfe9a27 100644 --- a/libbe/bugdir.py +++ b/libbe/bugdir.py @@ -141,7 +141,7 @@ class BugDir: except NoSuchFile: self.settings = {"rcs_name": "None"} - rcs_name = setting_property("rcs_name", ("None", "bzr", "Arch")) + rcs_name = setting_property("rcs_name", ("None", "bzr", "Arch", "hg")) _rcs = None target = setting_property("target") @@ -287,8 +287,11 @@ class Bug(object): return Comment(uuid, self) def iter_comment_ids(self): + path = self.get_path("comments") + if not os.path.isdir(path): + return try: - for uuid in os.listdir(self.get_path("comments")): + for uuid in os.listdir(path): if (uuid.startswith('.')): continue yield uuid diff --git a/libbe/bzr.py b/libbe/bzr.py index b53429c..ddda334 100644 --- a/libbe/bzr.py +++ b/libbe/bzr.py @@ -25,14 +25,7 @@ def invoke_client(*args, **kwargs): expect = kwargs.get('expect', (0, 1)) cl_args = ["bzr"] cl_args.extend(args) - if directory: - old_dir = os.getcwd() - os.chdir(directory) - try: - status,output,error = invoke(cl_args, expect) - finally: - if directory: - os.chdir(old_dir) + status,output,error = invoke(cl_args, expect, cwd=directory) return status, output def add_id(filename, paranoid=False): diff --git a/libbe/hg.py b/libbe/hg.py new file mode 100644 index 0000000..35de8e0 --- /dev/null +++ b/libbe/hg.py @@ -0,0 +1,115 @@ +# Copyright (C) 2007 Steve Borho <steve@borho.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +import os +import tempfile + +import config +from rcs import invoke, CommandError + +def invoke_client(*args, **kwargs): + directory = kwargs['directory'] + expect = kwargs.get('expect', (0, 1)) + cl_args = ["hg"] + cl_args.extend(args) + status,output,error = invoke(cl_args, expect, cwd=directory) + return status, output + +def add_id(filename, paranoid=False): + invoke_client("add", filename, directory='.') + +def delete_id(filename): + invoke_client("rm", filename, directory='.') + +def mkdir(path, paranoid=False): + os.mkdir(path) + +def set_file_contents(path, contents): + add = not os.path.exists(path) + file(path, "wb").write(contents) + if add: + add_id(path) + +def lookup_revision(revno, directory): + return invoke_client('log', '--rev', str(revno), '--template={node}', + directory=directory)[1].rstrip('\n') + +def export(revno, directory, revision_dir): + invoke_client("archive", "--rev", str(revno), revision_dir, + directory=directory) + +def find_or_make_export(revno, directory): + revision_id = lookup_revision(revno, directory) + home = os.path.expanduser("~") + revision_root = os.path.join(home, ".be_revs") + if not os.path.exists(revision_root): + os.mkdir(revision_root) + revision_dir = os.path.join(revision_root, revision_id) + if not os.path.exists(revision_dir): + export(revno, directory, revision_dir) + return revision_dir + +def hg_root(path): + return invoke_client("root", "-R", path, directory=None)[1].rstrip('\r') + +def path_in_reference(bug_dir, spec): + if spec is None: + spec = int(invoke_client('tip', '--template="{rev}"', + directory=bug_dir)[1]) + rel_bug_dir = bug_dir[len(hg_root(bug_dir)):] + export_root = find_or_make_export(spec, directory=bug_dir) + return os.path.join(export_root, rel_bug_dir) + + +def unlink(path): + try: + os.unlink(path) + delete_id(path) + except OSError, e: + if e.errno != 2: + raise + + +def detect(path): + """Detect whether a directory is revision-controlled using Mercurial""" + path = os.path.realpath(path) + old_path = None + while True: + if os.path.exists(os.path.join(path, ".hg")): + return True + if path == old_path: + return False + old_path = path + path = os.path.dirname(path) + +def precommit(directory): + pass + +def commit(directory, summary, body=None): + if body is not None: + summary += '\n' + body + descriptor, filename = tempfile.mkstemp() + try: + temp_file = os.fdopen(descriptor, 'wb') + temp_file.write(summary) + temp_file.close() + invoke_client('commit', '--logfile', filename, directory=directory) + finally: + os.unlink(filename) + +def postcommit(directory): + pass + +name = "hg" diff --git a/libbe/names.py b/libbe/names.py index cbcfbf8..d2e077a 100644 --- a/libbe/names.py +++ b/libbe/names.py @@ -14,13 +14,21 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -import commands + import os import sys def uuid(): - return commands.getoutput('uuidgen') + # this code borrowed from standard commands module + # but adapted to win32 + pipe = os.popen('uuidgen', 'r') + text = pipe.read() + sts = pipe.close() + if sts not in (0, None): + raise "Failed to run uuidgen" + if text[-1:] == '\n': text = text[:-1] + return text def creator(): if sys.platform != "win32": diff --git a/libbe/rcs.py b/libbe/rcs.py index ac96734..64503db 100644 --- a/libbe/rcs.py +++ b/libbe/rcs.py @@ -15,6 +15,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from subprocess import Popen, PIPE +import sys + def rcs_by_name(rcs_name): """Return the module for the RCS with the given name""" if rcs_name == "Arch": @@ -23,6 +25,9 @@ def rcs_by_name(rcs_name): elif rcs_name == "bzr": import bzr return bzr + elif rcs_name == "hg": + import hg + return hg elif rcs_name == "None": import no_rcs return no_rcs @@ -31,10 +36,13 @@ def detect(dir): """Return the module for the rcs being used in this directory""" import arch import bzr + import hg if arch.detect(dir): return arch elif bzr.detect(dir): return bzr + elif hg.detect(dir): + return hg import no_rcs return no_rcs @@ -44,10 +52,14 @@ class CommandError(Exception): self.err_str = err_str self.status = status -def invoke(args, expect=(0,)): - q = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) - output = q.stdout.read() - error = q.stderr.read() +def invoke(args, expect=(0,), cwd=None): + if sys.platform != "win32": + q = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=cwd) + else: + # win32 don't have os.execvp() so have to run command in a shell + q = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True, + cwd=cwd) + output, error = q.communicate() status = q.wait() if status not in expect: raise CommandError(error, status) |