Initial start page implementation

This commit is contained in:
Marco Pesenti Gritti 2006-06-14 15:01:17 -04:00
parent 4a7aac0e01
commit 3ea146e17c
10 changed files with 4788 additions and 8 deletions

View File

@ -0,0 +1,85 @@
"""
Facade that hides the differences between the SOAPpy and SOAP.py
libraries, so that google.py doesn't have to deal with them.
@author: Brian Landers <brian@bluecoat93.org>
@license: Python
@version: 0.5.4
"""
import warnings
from distutils.version import LooseVersion
__author__ = "Brian Landers <brian@bluecoat93.org>"
__version__ = "0.6"
__license__ = "Python"
#
# Wrapper around the python 'warnings' facility
#
def warn( message, level=RuntimeWarning ):
warnings.warn( message, level, stacklevel=3 )
# We can't use older version of SOAPpy, due to bugs that break the Google API
minSOAPpyVersion = "0.11.3"
#
# Try loading SOAPpy first. If that fails, fall back to the old SOAP.py
#
SOAPpy = None
try:
import SOAPpy
from SOAPpy import SOAPProxy, Types
if LooseVersion( minSOAPpyVersion ) > \
LooseVersion( SOAPpy.version.__version__ ):
warn( "Versions of SOAPpy before %s have known bugs that prevent " +
"PyGoogle from functioning." % minSOAPpyVersion )
raise ImportError
except ImportError:
warn( "SOAPpy not imported. Trying legacy SOAP.py.",
DeprecationWarning )
try:
import SOAP
except ImportError:
raise RuntimeError( "Unable to find SOAPpy or SOAP. Can't continue.\n" )
#
# Constants that differ between the modules
#
if SOAPpy:
false = Types.booleanType(0)
true = Types.booleanType(1)
structType = Types.structType
faultType = Types.faultType
else:
false = SOAP.booleanType(0)
true = SOAP.booleanType(1)
structType = SOAP.structType
faultType = SOAP.faultType
#
# Get a SOAP Proxy object in the correct way for the module we're using
#
def getProxy( url, namespace, http_proxy ):
if SOAPpy:
return SOAPProxy( url,
namespace = namespace,
http_proxy = http_proxy )
else:
return SOAP.SOAPProxy( url,
namespace = namespace,
http_proxy = http_proxy )
#
# Convert an object to a dictionary in the proper way for the module
# we're using for SOAP
#
def toDict( obj ):
if SOAPpy:
return obj._asdict()
else:
return obj._asdict

Binary file not shown.

3974
cut-n-paste/SOAP.py Executable file

File diff suppressed because it is too large Load Diff

BIN
cut-n-paste/SOAP.pyc Normal file

Binary file not shown.

638
cut-n-paste/google.py Executable file
View File

@ -0,0 +1,638 @@
"""
Python wrapper for Google web APIs
This module allows you to access Google's web APIs through SOAP,
to do things like search Google and get the results programmatically.
Described U{here <http://www.google.com/apis/>}
You need a Google-provided license key to use these services.
Follow the link above to get one. These functions will look in
several places (in this order) for the license key:
- the "license_key" argument of each function
- the module-level LICENSE_KEY variable (call setLicense once to set it)
- an environment variable called GOOGLE_LICENSE_KEY
- a file called ".googlekey" in the current directory
- a file called "googlekey.txt" in the current directory
- a file called ".googlekey" in your home directory
- a file called "googlekey.txt" in your home directory
- a file called ".googlekey" in the same directory as google.py
- a file called "googlekey.txt" in the same directory as google.py
Sample usage::
>>> import google
>>> google.setLicense('...') # must get your own key!
>>> data = google.doGoogleSearch('python')
>>> data.meta.searchTime
0.043221000000000002
>>> data.results[0].URL
'http://www.python.org/'
>>> data.results[0].title
'<b>Python</b> Language Website'
@newfield contrib: Contributors
@author: Mark Pilgrim <f8dy@diveintomark.org>
@author: Brian Landers <brian@bluecoat93.org>
@license: Python
@version: 0.6
@contrib: David Ascher, for the install script
@contrib: Erik Max Francis, for the command line interface
@contrib: Michael Twomey, for HTTP proxy support
@contrib: Mark Recht, for patches to support SOAPpy
"""
__author__ = "Mark Pilgrim (f8dy@diveintomark.org)"
__version__ = "0.6"
__cvsversion__ = "$Revision: 1.5 $"[11:-2]
__date__ = "$Date: 2004/02/25 23:46:07 $"[7:-2]
__copyright__ = "Copyright (c) 2002 Mark Pilgrim"
__license__ = "Python"
__credits__ = """David Ascher, for the install script
Erik Max Francis, for the command line interface
Michael Twomey, for HTTP proxy support"""
import os, sys, getopt
import GoogleSOAPFacade
LICENSE_KEY = None
HTTP_PROXY = None
#
# Constants
#
_url = 'http://api.google.com/search/beta2'
_namespace = 'urn:GoogleSearch'
_googlefile1 = ".googlekey"
_googlefile2 = "googlekey.txt"
_false = GoogleSOAPFacade.false
_true = GoogleSOAPFacade.true
_licenseLocations = (
( lambda key: key,
'passed to the function in license_key variable' ),
( lambda key: LICENSE_KEY,
'module-level LICENSE_KEY variable (call setLicense to set it)' ),
( lambda key: os.environ.get( 'GOOGLE_LICENSE_KEY', None ),
'an environment variable called GOOGLE_LICENSE_KEY' ),
( lambda key: _contentsOf( os.getcwd(), _googlefile1 ),
'%s in the current directory' % _googlefile1),
( lambda key: _contentsOf( os.getcwd(), _googlefile2 ),
'%s in the current directory' % _googlefile2),
( lambda key: _contentsOf( os.environ.get( 'HOME', '' ), _googlefile1 ),
'%s in your home directory' % _googlefile1),
( lambda key: _contentsOf( os.environ.get( 'HOME', '' ), _googlefile2 ),
'%s in your home directory' % _googlefile2 ),
( lambda key: _contentsOf( _getScriptDir(), _googlefile1 ),
'%s in the google.py directory' % _googlefile1 ),
( lambda key: _contentsOf( _getScriptDir(), _googlefile2 ),
'%s in the google.py directory' % _googlefile2 )
)
## ----------------------------------------------------------------------
## Exceptions
## ----------------------------------------------------------------------
class NoLicenseKey(Exception):
"""
Thrown when the API is unable to find a valid license key.
"""
pass
## ----------------------------------------------------------------------
## administrative functions (non-API)
## ----------------------------------------------------------------------
def _version():
"""
Display a formatted version string for the module
"""
print """PyGoogle %(__version__)s
%(__copyright__)s
released %(__date__)s
Thanks to:
%(__credits__)s""" % globals()
def _usage():
"""
Display usage information for the command-line interface
"""
program = os.path.basename(sys.argv[0])
print """Usage: %(program)s [options] [querytype] query
options:
-k, --key= <license key> Google license key (see important note below)
-1, -l, --lucky show only first hit
-m, --meta show meta information
-r, --reverse show results in reverse order
-x, --proxy= <url> use HTTP proxy
-h, --help print this help
-v, --version print version and copyright information
-t, --test run test queries
querytype:
-s, --search= <query> search (default)
-c, --cache= <url> retrieve cached page
-p, --spelling= <word> check spelling
IMPORTANT NOTE: all Google functions require a valid license key;
visit http://www.google.com/apis/ to get one. %(program)s will look in
these places (in order) and use the first license key it finds:
* the key specified on the command line""" % vars()
for get, location in _licenseLocations[2:]:
print " *", location
## ----------------------------------------------------------------------
## utility functions (API)
## ----------------------------------------------------------------------
def setLicense(license_key):
"""
Set the U{Google APIs <http://www.google.com/api>} license key
@param license_key: The new key to use
@type license_key: String
@todo: validate the key?
"""
global LICENSE_KEY
LICENSE_KEY = license_key
def getLicense(license_key = None):
"""
Get the U{Google APIs <http://www.google.com/api>} license key
The key can be read from any number of locations. See the module-leve
documentation for the search order.
@return: the license key
@rtype: String
@raise NoLicenseKey: if no valid key could be found
"""
for get, location in _licenseLocations:
rc = get(license_key)
if rc: return rc
_usage()
raise NoLicenseKey, 'get a license key at http://www.google.com/apis/'
def setProxy(http_proxy):
"""
Set the HTTP proxy to be used when accessing Google
@param http_proxy: the proxy to use
@type http_proxy: String
@todo: validiate the input?
"""
global HTTP_PROXY
HTTP_PROXY = http_proxy
def getProxy(http_proxy = None):
"""
Get the HTTP proxy we use for accessing Google
@return: the proxy
@rtype: String
"""
return http_proxy or HTTP_PROXY
def _contentsOf(dirname, filename):
filename = os.path.join(dirname, filename)
if not os.path.exists(filename): return None
fsock = open(filename)
contents = fsock.read()
fsock.close()
return contents
def _getScriptDir():
if __name__ == '__main__':
return os.path.abspath(os.path.dirname(sys.argv[0]))
else:
return os.path.abspath(os.path.dirname(sys.modules[__name__].__file__))
def _marshalBoolean(value):
if value:
return _true
else:
return _false
def _getRemoteServer( http_proxy ):
return GoogleSOAPFacade.getProxy( _url, _namespace, http_proxy )
## ----------------------------------------------------------------------
## search results classes
## ----------------------------------------------------------------------
class _SearchBase:
def __init__(self, params):
for k, v in params.items():
if isinstance(v, GoogleSOAPFacade.structType):
v = GoogleSOAPFacade.toDict( v )
try:
if isinstance(v[0], GoogleSOAPFacade.structType):
v = [ SOAPProxy.toDict( node ) for node in v ]
except:
pass
self.__dict__[str(k)] = v
## ----------------------------------------------------------------------
class SearchResultsMetaData(_SearchBase):
"""
Container class for metadata about a given search query's results.
@ivar documentFiltering: is duplicate page filtering active?
@ivar searchComments: human-readable informational message
example::
"'the' is a very common word and was not included in your search"
@ivar estimatedTotalResultsCount: estimated total number of results
for this query.
@ivar estimateIsExact: is estimatedTotalResultsCount an exact value?
@ivar searchQuery: search string that initiated this search
@ivar startIndex: index of the first result returned (zero-based)
@ivar endIndex: index of the last result returned (zero-based)
@ivar searchTips: human-readable informational message on how to better
use Google.
@ivar directoryCategories: list of categories for the search results
This field is a list of dictionaries, like so::
{ 'fullViewableName': 'the Open Directory category',
'specialEncoding': 'encoding scheme of this directory category'
}
@ivar searchTime: total search time, in seconds
"""
pass
## ----------------------------------------------------------------------
class SearchResult(_SearchBase):
"""
Encapsulates the results from a search.
@ivar URL: URL
@ivar title: title (HTML)
@ivar snippet: snippet showing query context (HTML
@ivar cachedSize: size of cached version of this result, (KB)
@ivar relatedInformationPresent: is the "related:" keyword supported?
Flag indicates that the "related:" keyword is supported for this URL
@ivar hostName: used when filtering occurs
When filtering occurs, a maximum of two results from any given
host is returned. When this occurs, the second resultElement
that comes from that host contains the host name in this parameter.
@ivar directoryCategory: Open Directory category information
This field is a dictionary with the following values::
{ 'fullViewableName': 'the Open Directory category',
'specialEncoding' : 'encoding scheme of this directory category'
}
@ivar directoryTitle: Open Directory title of this result (or blank)
@ivar summary: Open Directory summary for this result (or blank)
"""
pass
## ----------------------------------------------------------------------
class SearchReturnValue:
"""
complete search results for a single query
@ivar meta: L{SearchResultsMetaData} instance for this query
@ivar results: list of L{SearchResult} objects for this query
"""
def __init__( self, metadata, results ):
self.meta = metadata
self.results = results
## ----------------------------------------------------------------------
## main functions
## ----------------------------------------------------------------------
def doGoogleSearch( q, start = 0, maxResults = 10, filter = 1,
restrict='', safeSearch = 0, language = '',
inputencoding = '', outputencoding = '',\
license_key = None, http_proxy = None ):
"""
Search Google using the SOAP API and return the results.
You need a license key to call this function; see the
U{Google APIs <http://www.google.com/apis/>} site to get one.
Then you can either pass it to this function every time, or
set it globally; see the L{google} module-level docs for details.
See U{http://www.google.com/help/features.html}
for examples of advanced features. Anything that works at the
Google web site will work as a query string in this method.
You can use the C{start} and C{maxResults} parameters to page
through multiple pages of results. Note that 'maxResults' is
currently limited by Google to 10.
See the API reference for more advanced examples and a full list of
country codes and topics for use in the C{restrict} parameter, along
with legal values for the C{language}, C{inputencoding}, and
C{outputencoding} parameters.
You can download the API documentation
U{http://www.google.com/apis/download.html <here>}.
@param q: search string.
@type q: String
@param start: (optional) zero-based index of first desired result.
@type start: int
@param maxResults: (optional) maximum number of results to return.
@type maxResults: int
@param filter: (optional) flag to request filtering of similar results
@type filter: int
@param restrict: (optional) restrict results by country or topic.
@type restrict: String
@param safeSearch: (optional)
@type safeSearch: int
@param language: (optional)
@type language: String
@param inputencoding: (optional)
@type inputencoding: String
@param outputencoding: (optional)
@type outputencoding: String
@param license_key: (optional) the Google API license key to use
@type license_key: String
@param http_proxy: (optional) the HTTP proxy to use for talking to Google
@type http_proxy: String
@return: the search results encapsulated in an object
@rtype: L{SearchReturnValue}
"""
license_key = getLicense( license_key )
http_proxy = getProxy( http_proxy )
remoteserver = _getRemoteServer( http_proxy )
filter = _marshalBoolean( filter )
safeSearch = _marshalBoolean( safeSearch )
data = remoteserver.doGoogleSearch( license_key, q, start, maxResults,
filter, restrict, safeSearch,
language, inputencoding,
outputencoding )
metadata = GoogleSOAPFacade.toDict( data )
del metadata["resultElements"]
metadata = SearchResultsMetaData( metadata )
results = [ SearchResult( GoogleSOAPFacade.toDict( node ) ) \
for node in data.resultElements ]
return SearchReturnValue( metadata, results )
## ----------------------------------------------------------------------
def doGetCachedPage( url, license_key = None, http_proxy = None ):
"""
Retrieve a page from the Google cache.
You need a license key to call this function; see the
U{Google APIs <http://www.google.com/apis/>} site to get one.
Then you can either pass it to this function every time, or
set it globally; see the L{google} module-level docs for details.
@param url: full URL to the page to retrieve
@type url: String
@param license_key: (optional) the Google API key to use
@type license_key: String
@param http_proxy: (optional) the HTTP proxy server to use
@type http_proxy: String
@return: full text of the cached page
@rtype: String
"""
license_key = getLicense( license_key )
http_proxy = getProxy( http_proxy )
remoteserver = _getRemoteServer( http_proxy )
return remoteserver.doGetCachedPage( license_key, url )
## ----------------------------------------------------------------------
def doSpellingSuggestion( phrase, license_key = None, http_proxy = None ):
"""
Get spelling suggestions from Google
You need a license key to call this function; see the
U{Google APIs <http://www.google.com/apis/>} site to get one.
Then you can either pass it to this function every time, or
set it globally; see the L{google} module-level docs for details.
@param phrase: word or phrase to spell-check
@type phrase: String
@param license_key: (optional) the Google API key to use
@type license_key: String
@param http_proxy: (optional) the HTTP proxy to use
@type http_proxy: String
@return: text of any suggested replacement, or None
"""
license_key = getLicense( license_key )
http_proxy = getProxy( http_proxy)
remoteserver = _getRemoteServer( http_proxy )
return remoteserver.doSpellingSuggestion( license_key, phrase )
## ----------------------------------------------------------------------
## functional test suite (see googletest.py for unit test suite)
## ----------------------------------------------------------------------
def _test():
"""
Run functional test suite.
"""
try:
getLicense(None)
except NoLicenseKey:
return
print "Searching for Python at google.com..."
data = doGoogleSearch( "Python" )
_output( data, { "func": "doGoogleSearch"} )
print "\nSearching for 5 _French_ pages about Python, "
print "encoded in ISO-8859-1..."
data = doGoogleSearch( "Python", language = 'lang_fr',
outputencoding = 'ISO-8859-1',
maxResults = 5 )
_output( data, { "func": "doGoogleSearch" } )
phrase = "Pyhton programming languager"
print "\nTesting spelling suggestions for '%s'..." % phrase
data = doSpellingSuggestion( phrase )
_output( data, { "func": "doSpellingSuggestion" } )
## ----------------------------------------------------------------------
## Command-line interface
## ----------------------------------------------------------------------
class _OutputFormatter:
def boil(self, data):
if type(data) == type(u""):
return data.encode("ISO-8859-1", "replace")
else:
return data
class _TextOutputFormatter(_OutputFormatter):
def common(self, data, params):
if params.get("showMeta", 0):
meta = data.meta
for category in meta.directoryCategories:
print "directoryCategory: %s" % \
self.boil(category["fullViewableName"])
for attr in [node for node in dir(meta) if \
node <> "directoryCategories" and node[:2] <> '__']:
print "%s:" % attr, self.boil(getattr(meta, attr))
def doGoogleSearch(self, data, params):
results = data.results
if params.get("feelingLucky", 0):
results = results[:1]
if params.get("reverseOrder", 0):
results.reverse()
for result in results:
for attr in dir(result):
if attr == "directoryCategory":
print "directoryCategory:", \
self.boil(result.directoryCategory["fullViewableName"])
elif attr[:2] <> '__':
print "%s:" % attr, self.boil(getattr(result, attr))
print
self.common(data, params)
def doGetCachedPage(self, data, params):
print data
self.common(data, params)
doSpellingSuggestion = doGetCachedPage
def _makeFormatter(outputFormat):
classname = "_%sOutputFormatter" % outputFormat.capitalize()
return globals()[classname]()
def _output(results, params):
formatter = _makeFormatter(params.get("outputFormat", "text"))
outputmethod = getattr(formatter, params["func"])
outputmethod(results, params)
def main(argv):
"""
Command-line interface.
"""
if not argv:
_usage()
return
q = None
func = None
http_proxy = None
license_key = None
feelingLucky = 0
showMeta = 0
reverseOrder = 0
runTest = 0
outputFormat = "text"
try:
opts, args = getopt.getopt(argv, "s:c:p:k:lmrx:hvt1",
["search=", "cache=", "spelling=", "key=", "lucky", "meta",
"reverse", "proxy=", "help", "version", "test"])
except getopt.GetoptError:
_usage()
sys.exit(2)
for opt, arg in opts:
if opt in ("-s", "--search"):
q = arg
func = "doGoogleSearch"
elif opt in ("-c", "--cache"):
q = arg
func = "doGetCachedPage"
elif opt in ("-p", "--spelling"):
q = arg
func = "doSpellingSuggestion"
elif opt in ("-k", "--key"):
license_key = arg
elif opt in ("-l", "-1", "--lucky"):
feelingLucky = 1
elif opt in ("-m", "--meta"):
showMeta = 1
elif opt in ("-r", "--reverse"):
reverseOrder = 1
elif opt in ("-x", "--proxy"):
http_proxy = arg
elif opt in ("-h", "--help"):
_usage()
elif opt in ("-v", "--version"):
_version()
elif opt in ("-t", "--test"):
runTest = 1
if runTest:
setLicense(license_key)
setProxy(http_proxy)
_test()
if args and not q:
q = args[0]
func = "doGoogleSearch"
if func:
results = globals()[func]( q, http_proxy=http_proxy,
license_key=license_key )
_output(results, locals())
if __name__ == '__main__':
main(sys.argv[1:])

BIN
cut-n-paste/google.pyc Normal file

Binary file not shown.

View File

@ -79,6 +79,7 @@ class Buddy(gobject.GObject):
# A buddy isn't valid until its official presence # A buddy isn't valid until its official presence
# service has been found and resolved # service has been found and resolved
self._valid = True self._valid = True
print 'Requesting buddy icon %s' % self._nick_name
self._request_buddy_icon(service) self._request_buddy_icon(service)
return True return True

80
sugar/shell/StartPage.py Normal file
View File

@ -0,0 +1,80 @@
import pygtk
pygtk.require('2.0')
import gtk
import dbus
import google
class ActivitiesModel(gtk.ListStore):
def __init__(self):
gtk.ListStore.__init__(self, str, str)
def add_web_page(self, title, address):
self.append([ title, address ])
class ActivitiesView(gtk.TreeView):
def __init__(self, model):
gtk.TreeView.__init__(self, model)
self.set_headers_visible(False)
column = gtk.TreeViewColumn('')
self.append_column(column)
cell = gtk.CellRendererText()
column.pack_start(cell, True)
column.add_attribute(cell, 'text', 0)
self.connect('row-activated', self._row_activated_cb)
def _row_activated_cb(self, treeview, path, column):
bus = dbus.SessionBus()
proxy_obj = bus.get_object('com.redhat.Sugar.Browser', '/com/redhat/Sugar/Browser')
browser_shell = dbus.Interface(proxy_obj, 'com.redhat.Sugar.BrowserShell')
model = self.get_model()
address = model.get_value(model.get_iter(path), 1)
browser_shell.open_browser(address)
class StartPage(gtk.HBox):
def __init__(self):
gtk.HBox.__init__(self)
vbox = gtk.VBox()
search_box = gtk.HBox(False, 6)
search_box.set_border_width(24)
self._search_entry = gtk.Entry()
search_box.pack_start(self._search_entry)
self._search_entry.show()
search_button = gtk.Button("Search")
search_button.connect('clicked', self._search_button_clicked_cb)
search_box.pack_start(search_button, False)
search_button.show()
vbox.pack_start(search_box, False, True)
search_box.show()
exp_space = gtk.Label('')
vbox.pack_start(exp_space)
exp_space.show()
self.pack_start(vbox)
vbox.show()
self._activities_model = ActivitiesModel()
activities = ActivitiesView(self._activities_model)
self.pack_start(activities)
activities.show()
def _search_button_clicked_cb(self, button):
self.search(self._search_entry.get_text())
def search(self, text):
google.LICENSE_KEY = '1As9KaJQFHIJ1L0W5EZPl6vBOFvh/Vaf'
data = google.doGoogleSearch(text)
for result in data.results:
self._activities_model.add_web_page(result.title, result.URL)

View File

@ -10,6 +10,7 @@ import pango
import sugar.util import sugar.util
from sugar.shell.PresenceWindow import PresenceWindow from sugar.shell.PresenceWindow import PresenceWindow
from sugar.shell.Owner import ShellOwner from sugar.shell.Owner import ShellOwner
from sugar.shell.StartPage import StartPage
class ActivityHost(dbus.service.Object): class ActivityHost(dbus.service.Object):
@ -226,10 +227,10 @@ class ActivityContainer(dbus.service.Object):
self.window.set_geometry_hints(min_width = 640, max_width = 640, min_height = 480, max_height = 480) self.window.set_geometry_hints(min_width = 640, max_width = 640, min_height = 480, max_height = 480)
self.notebook = gtk.Notebook() self.notebook = gtk.Notebook()
#tab_label = gtk.Label("My Laptop") tab_label = gtk.Label("Everyone")
#empty_label = gtk.Label("This activity could launch other activities / be a help page") tab_page = StartPage()
#empty_label.show() self.notebook.append_page(tab_page, tab_label)
#self.notebook.append_page(empty_label, tab_label) tab_page.show()
self.notebook.show() self.notebook.show()
self.notebook.connect("switch-page", self.notebook_tab_changed) self.notebook.connect("switch-page", self.notebook_tab_changed)

View File

@ -7,7 +7,8 @@ import pygtk
pygtk.require('2.0') pygtk.require('2.0')
import gobject import gobject
def append_to_python_path(path): def add_to_python_path(path):
sys.path.insert(0, path)
if os.environ.has_key('PYTHONPATH'): if os.environ.has_key('PYTHONPATH'):
os.environ['PYTHONPATH'] += ':' + path os.environ['PYTHONPATH'] += ':' + path
else: else:
@ -59,13 +60,13 @@ if os.path.isfile(os.path.join(curdir, '__uninstalled__.py')):
print 'Running sugar from current directory...' print 'Running sugar from current directory...'
else: else:
print 'Running sugar from ' + basedir + ' ...' print 'Running sugar from ' + basedir + ' ...'
sys.path.insert(0, basedir) add_to_python_path(basedir)
append_to_python_path(basedir) add_to_python_path(os.path.join(basedir, 'cut-n-paste'))
console = True console = True
else: else:
print 'Running the installed sugar...' print 'Running the installed sugar...'
append_to_python_path(os.path.expanduser('~/.sugar/activities')) add_to_python_path(os.path.expanduser('~/.sugar/activities'))
if console: if console:
os.environ['SUGAR_USE_CONSOLE'] = 'yes' os.environ['SUGAR_USE_CONSOLE'] = 'yes'