Merge changes from my devel branch:

- ubuntutools/lp/lpapicache.py, ubuntutools/lp/libsupport.py: Add support
  for different LP API versions.
- ubuntutools/lp/__init__.py: Set the '1.0' LP API version as default.
- massfile: Updated to 1.0 LP API.
- doc/requestsync.1: Update the paragraph about sponsoring (lp: #538990).
- pull-lp-source: Use (anonymously) the LP API to get the URL for the .dsc
  file instead of screen scraping.
- ubuntutools/requestsync/mail.py: Fix some more encoding issues.
- Apply patch from Julian Andres Klode for the python-apt 0.8 API transition
  (Closes: #572091)
This commit is contained in:
Michael Bienia 2010-03-25 22:16:20 +01:00
commit 28f7abc2d4
14 changed files with 136 additions and 124 deletions

16
404main
View File

@ -88,19 +88,19 @@ def find_main(cache, pack):
# attribute on version for this, so unfortunately we have to # attribute on version for this, so unfortunately we have to
# do a lot of messing about with apt. # do a lot of messing about with apt.
deps = [] deps = []
src_records = apt_pkg.GetPkgSrcRecords() src_records = apt_pkg.SourceRecords()
got_src = False got_src = False
while src_records.Lookup(version.source_name): while src_records.lookup(version.source_name):
if pack in src_records.Binaries: if pack in src_records.binaries:
got_src = True got_src = True
break break
if got_src: if got_src:
base_deps = [] for deptype, all_deps in src_records.build_depends.iteritems():
for (name, ver, op, deptype) in src_records.BuildDepends: for or_deps in all_deps:
base_deps.append(apt.package.BaseDependency(name, comp_type_deb(op), ver, False))
if (op & 16) != 16:
deps.append(apt.package.Dependency(base_deps))
base_deps = [] base_deps = []
for (name, ver, op) in or_deps:
base_deps.append(apt.package.BaseDependency(name, op, ver, False))
deps.append(apt.package.Dependency(base_deps))
process_deps(cache, deps) process_deps(cache, deps)

11
debian/changelog vendored
View File

@ -3,8 +3,17 @@ ubuntu-dev-tools (0.97) UNRELEASED; urgency=low
[ Michael Bienia ] [ Michael Bienia ]
* lp-shell: Support all to the launchpadlib Python module known service * lp-shell: Support all to the launchpadlib Python module known service
names. names.
* ubuntutools/lp/lpapicache.py, ubuntutools/lp/libsupport.py: Add support
for different LP API versions.
* ubuntutools/lp/__init__.py: Set the '1.0' LP API version as default.
* massfile: Updated to 1.0 LP API.
* doc/requestsync.1: Update the paragraph about sponsoring (lp: #538990).
* pull-lp-source: Use (anonymously) the LP API to get the URL for the .dsc
file instead of screen scraping.
* Apply patch from Julian Andres Klode for the python-apt 0.8 API transition
(Closes: #572091)
-- Michael Bienia <geser@ubuntu.com> Thu, 18 Mar 2010 11:01:21 +0100 -- Michael Bienia <geser@ubuntu.com> Thu, 25 Mar 2010 21:58:47 +0100
ubuntu-dev-tools (0.96) lucid; urgency=low ubuntu-dev-tools (0.96) lucid; urgency=low

2
debian/control vendored
View File

@ -14,7 +14,7 @@ Package: ubuntu-dev-tools
Architecture: all Architecture: all
Depends: ${python:Depends}, ${misc:Depends}, binutils, devscripts, sudo, Depends: ${python:Depends}, ${misc:Depends}, binutils, devscripts, sudo,
python-debian, python-launchpadlib (>= 1.5.4), dctrl-tools, lsb-release, diffstat, python-debian, python-launchpadlib (>= 1.5.4), dctrl-tools, lsb-release, diffstat,
dpkg-dev, python-apt (>= 0.7.9), python-lazr.restfulclient dpkg-dev, python-apt (>= 0.7.93~), python-lazr.restfulclient
Recommends: bzr, pbuilder | cowdancer | sbuild, reportbug (>= 3.39ubuntu1), Recommends: bzr, pbuilder | cowdancer | sbuild, reportbug (>= 3.39ubuntu1),
ca-certificates, debootstrap, genisoimage, perl-modules, libwww-perl, ca-certificates, debootstrap, genisoimage, perl-modules, libwww-perl,
libapt-pkg-perl libapt-pkg-perl

View File

@ -2,7 +2,7 @@
.SH NAME .SH NAME
requestsync \- helper to file sync requests for Ubuntu requestsync \- helper to file sync requests for Ubuntu
.SH SYNOPSIS .SH SYNOPSIS
.B requestsync\fR [\fB\-d distro\fR] [\fB\-nse\fR] [\fB\-k \fIkeyid\fR] <\fBsource package\fR> [\fBtarget release\fR] [\fIbase version\fR] .B requestsync\fR [\fB\-d \fIdistro\fR] [\fB\-nse\fR] [\fB\-k \fIkeyid\fR] <\fBsource package\fR> [\fBtarget release\fR] [\fIbase version\fR]
.br .br
.B requestsync \-\-lp\fR [\fB\-nse\fR] <\fBsource package\fR> <\fBtarget release\fR> [\fIbase version\fR] .B requestsync \-\-lp\fR [\fB\-nse\fR] <\fBsource package\fR> <\fBtarget release\fR> [\fIbase version\fR]
.br .br
@ -22,10 +22,15 @@ the launchpadlib module fails.
.PP .PP
\fBrequestsync\fR checks if you have the permissions to request the sync from \fBrequestsync\fR checks if you have the permissions to request the sync from
the archive administrators directly by checking if you are a member of the the archive administrators directly by checking if you have upload permissions
\fI~ubuntu\-dev\fR team on Launchpad. for that package through package set permissions or component permissions. If
If you are not a member of the team, the script will subscribe you don't have upload permissions, the script will subscribe the necessary
the necessary team with approval rights to the bug report for you. team with approval rights to the bug report for you.
This check is only performed if \fBrequestsync\fR is allowed to use the LP API
(option \fB\-\-lp\fR). In the other case \fBrequestsync\fR relies on that you
answer the question about upload permissions honestly to determine if a team
with approval rights is to be subscribed to the bug.
.PP .PP
\fBrequestsync\fR uses launchpadlib authentication to file its requests. Please \fBrequestsync\fR uses launchpadlib authentication to file its requests. Please
@ -57,8 +62,8 @@ file the sync request in Launchpad.
.TP .TP
.B \-s .B \-s
Specifies that you require sponsorship. Specifies that you require sponsorship.
You need this option if you are not a member of ubuntu-dev. This shall disable the You need this option if you don't have upload permissions for that package.
Launchpad team membership checking described above. This disables the upload permissions check described above.
.TP .TP
.B \-e .B \-e
Use this flag after FeatureFreeze for non-bug fix syncs. \fBrequestsync\fR will Use this flag after FeatureFreeze for non-bug fix syncs. \fBrequestsync\fR will
@ -96,7 +101,7 @@ If unspecified this defaults to fiordland.ubuntu.com.
.B DEBSMTP_PORT .B DEBSMTP_PORT
Sets which port of the SMTP server to use. Default is 25. Sets which port of the SMTP server to use. Default is 25.
.TP .TP
.B DEBSMTP_USER and DEBSMTP_PASS .B DEBSMTP_USER \fRand\fB DEBSMTP_PASS
Sets the username and password to use when authenticating to the SMTP server. Sets the username and password to use when authenticating to the SMTP server.
.SH SEE ALSO .SH SEE ALSO

View File

@ -106,12 +106,13 @@ def file_bug(config):
status = config["status"].capitalize() status = config["status"].capitalize()
else: else:
status = "Confirmed" status = "Confirmed"
task.transitionToStatus(status=status) task.status = status
assignee = config["assignee"] assignee = config["assignee"]
if assignee: if assignee:
assignee_url = "%s~%s" %(launchpad._root_uri, assignee) assignee_url = "%s~%s" %(launchpad._root_uri, assignee)
bug.transitionToAssignee(assignee=assignee_url) task.assignee = assignee_url
task.lp_save()
except: except:
"Bug for '%s' was not filed." % config["sourcepackage"] "Bug for '%s' was not filed." % config["sourcepackage"]

View File

@ -26,84 +26,60 @@
import os import os
import re
import subprocess
import sys import sys
import urllib2 import subprocess
from optparse import OptionParser from optparse import OptionParser
# ubuntu-dev-tools modules. # ubuntu-dev-tools modules.
from ubuntutools.lp.lpapicache import Distribution from ubuntutools.lp.lpapicache import Distribution, Launchpad
from ubuntutools.lp.udtexceptions import SeriesNotFoundException, PackageNotFoundException from ubuntutools.lp.udtexceptions import (SeriesNotFoundException,
PackageNotFoundException, PocketDoesNotExistError)
from ubuntutools.misc import splitReleasePocket
if not os.path.exists("/usr/bin/dget"): if not os.path.exists("/usr/bin/dget"):
print "dget is not installed - please install the 'devscripts' package" \ print "E: dget is not installed - please install the 'devscripts' package" \
" and rerun this script again." " and rerun this script again."
sys.exit(1) sys.exit(1)
class BackportFromLP:
def __getitem__(self, name):
return getattr(self, name)
def __init__(self, package, target_release):
self.package = package
self.target_release = target_release
self.__prepare_sources()
def __prepare_sources(self):
# Scrape the source package from Launchpad :)
try:
contents = urllib2.urlopen('https://launchpad.net/ubuntu/%(target_release)s/+source/%(package)s' % self).read()
links = re.findall('href=\"(.*\.dsc)\"', contents)
if len(links) == 1 and \
subprocess.call(['dget', '-xu', 'https://launchpad.net%s' % links[0]]) == 0:
print '\nSuccess!'
else:
raise ValueError, '\nFailed to fetch and extract the source. ' +\
'Ensure that the package specified is a valid source ' +\
'package name and that Launchpad is not down.'
except urllib2.HTTPError:
raise ValueError, '\nFailed to fetch and extract the source. ' +\
'Ensure that the package specified is a valid source ' +\
'package name and that Launchpad is not down.'
if __name__ == '__main__': if __name__ == '__main__':
usage = "Usage: %prog <package> [release]" usage = "Usage: %prog <package> [release]"
optParser = OptionParser(usage) optParser = OptionParser(usage)
(options, args) = optParser.parse_args() (options, args) = optParser.parse_args()
release = None
if not args: optParser.error("Arguments required.") if not args:
optParser.print_help()
sys.exit(1)
# Login anonymously to LP
Launchpad.login_anonymously()
package = str(args[0]).lower() package = str(args[0]).lower()
try: if len(args) >= 2: # Custom distribution specified.
if len(args) == 2: # Custom distribution specified. release = str(args[1]).lower()
release = str(args[1]).lower() else:
else: release = os.getenv('DIST') or Distribution('ubuntu').getDevelopmentSeries().name
release = os.getenv('DIST') or Distribution('ubuntu').getDevelopmentSeries().name
# Arguments are correct, proceed. try:
# Check that the Ubuntu release and package specified exists. (release, pocket) = splitReleasePocket(release)
Distribution('ubuntu').getArchive().getSourcePackage(package, release) except PocketDoesNotExistError, e:
except IOError, e: print 'E: %s' % e
print "This is non-fatal, continuing..."
except (SeriesNotFoundException, PackageNotFoundException), e:
print e
sys.exit(1) sys.exit(1)
# We won't have been able to get the release if there are no LP credentials, hardcode it try:
# also could bail out here if others think that is better spph = Distribution('ubuntu').getArchive().getSourcePackage(package, release, pocket)
if release is None: except (SeriesNotFoundException, PackageNotFoundException), e:
release = 'lucid' print 'E: %s' % e
sys.exit(1)
dsc_url = [url for url in spph.sourceFileUrls() if url.endswith('.dsc')]
assert dsc_url, 'No .dsc file found'
# All good - start downloading... # All good - start downloading...
try: print 'Fetching the source for %s from %s (%s)...' % (
print 'Attempting to get %s from release %s...' % \ package, release.capitalize(), pocket)
(package, release.capitalize()) if subprocess.call(['/usr/bin/dget', '-xu', dsc_url[0]]) == 0:
BackportFromLP(package, release) print 'Success!'
except ValueError, e: else:
print 'Error when downloading package %s from release %s: %s.' % \ print 'Failed to fetch and extrace the source.', \
(package, release, e) 'Please check the output for the error.'

View File

@ -26,8 +26,10 @@
import sys import sys
from optparse import OptionGroup from optparse import OptionGroup
from optparse import OptionParser from optparse import OptionParser
from ubuntutools.lp.udtexceptions import SeriesNotFoundException, PackageNotFoundException from ubuntutools.lp.udtexceptions import (SeriesNotFoundException,
PackageNotFoundException, PocketDoesNotExistError,)
from ubuntutools.lp.lpapicache import Distribution, PersonTeam from ubuntutools.lp.lpapicache import Distribution, PersonTeam
from ubuntutools.misc import splitReleasePocket
# Usage. # Usage.
usage = "%prog <srcpackage> <release> <operation>\n\n" usage = "%prog <srcpackage> <release> <operation>\n\n"
@ -114,13 +116,10 @@ if not options.batch:
oneArch = False oneArch = False
# split release and pocket # split release and pocket
if '-' in release: try:
(release, pocket) = release.split('-') (release, pocket) = splitReleasePocket(release)
else: except PocketDoesNotExistError, e:
pocket = 'Release' print 'E: %s' % e
pocket = pocket.capitalize()
if pocket not in ('Release', 'Security', 'Updates', 'Proposed', 'Backports'):
print 'Unknown pocket: %s' % pocket
sys.exit(1) sys.exit(1)
# Get the ubuntu archive # Get the ubuntu archive
@ -205,15 +204,11 @@ else:
archs.intersection_update(valid_archs) archs.intersection_update(valid_archs)
release = options.series or Distribution('ubuntu').getDevelopmentSeries().name release = options.series or Distribution('ubuntu').getDevelopmentSeries().name
pocket = 'Release' try:
if release and '-' in release: (release, pocket) = splitReleasePocket(release)
# split release and pocket except PocketDoesNotExistError, e:
(release, pocket) = options.series.split('-') print 'E: %s' % e
pocket = pocket.capitalize() sys.exit(1)
if pocket not in ('Release', 'Security', 'Updates', 'Proposed', 'Backports'):
print 'Unknown pocket: %s' % pocket
sys.exit(1)
ubuntu_archive = Distribution('ubuntu').getArchive() ubuntu_archive = Distribution('ubuntu').getArchive()
try: try:

View File

@ -3,3 +3,4 @@
## ##
service = 'edge' service = 'edge'
api_version = '1.0'

View File

@ -37,7 +37,7 @@ except:
Credentials = None Credentials = None
Launchpad = None Launchpad = None
from ubuntutools.lp import service from ubuntutools.lp import (service, api_version)
def find_credentials(consumer, files, level=None): def find_credentials(consumer, files, level=None):
""" search for credentials matching 'consumer' in path for given access level. """ """ search for credentials matching 'consumer' in path for given access level. """
@ -79,7 +79,7 @@ def get_launchpad(consumer, server=service, cache=None,
cred_file=None, level=None): cred_file=None, level=None):
credentials = get_credentials(consumer, cred_file, level) credentials = get_credentials(consumer, cred_file, level)
cache = cache or os.environ.get("LPCACHE", None) cache = cache or os.environ.get("LPCACHE", None)
return Launchpad(credentials, server, cache) return Launchpad(credentials, server, cache, version=api_version)
def query_to_dict(query_string): def query_to_dict(query_string):
result = dict() result = dict()
@ -106,7 +106,7 @@ def translate_web_api(url, launchpad):
return url return url
def translate_api_web(self_url): def translate_api_web(self_url):
return self_url.replace("api.", "").replace("beta/", "") return self_url.replace("api.", "").replace("%s/" % (api_version), "")
LEVEL = { LEVEL = {
0: "UNAUTHORIZED", 0: "UNAUTHORIZED",

View File

@ -32,7 +32,7 @@ from launchpadlib.uris import lookup_service_root
from lazr.restfulclient.resource import Entry from lazr.restfulclient.resource import Entry
import ubuntutools.lp.libsupport as libsupport import ubuntutools.lp.libsupport as libsupport
from ubuntutools.lp import service from ubuntutools.lp import (service, api_version)
from ubuntutools.lp.udtexceptions import * from ubuntutools.lp.udtexceptions import *
__all__ = [ __all__ = [
@ -63,7 +63,8 @@ class Launchpad(object):
def login_anonymously(self): def login_anonymously(self):
'''Enforce an anonymous login.''' '''Enforce an anonymous login.'''
if '_Launchpad__lp' not in self.__dict__: if '_Launchpad__lp' not in self.__dict__:
self.__lp = launchpad.Launchpad.login_anonymously('ubuntu-dev-tools', service) self.__lp = launchpad.Launchpad.login_anonymously('ubuntu-dev-tools',
service_root=service, version=api_version)
else: else:
raise AlreadyLoggedInError('Already logged in to Launchpad.') raise AlreadyLoggedInError('Already logged in to Launchpad.')
@ -96,7 +97,7 @@ class BaseWrapper(object):
resource_type = None # it's a base class after all resource_type = None # it's a base class after all
def __new__(cls, data): def __new__(cls, data):
if isinstance(data, basestring) and data.startswith(lookup_service_root(service) + 'beta/'): if isinstance(data, basestring) and data.startswith('%s%s/' % (lookup_service_root(service), api_version)):
# looks like a LP API URL # looks like a LP API URL
# check if it's already cached # check if it's already cached
cached = cls._cache.get(data) cached = cls._cache.get(data)
@ -151,7 +152,7 @@ class Distribution(BaseWrapper):
''' '''
Wrapper class around a LP distribution object. Wrapper class around a LP distribution object.
''' '''
resource_type = lookup_service_root(service) + 'beta/#distribution' resource_type = lookup_service_root(service) + api_version + '/#distribution'
def __init__(self, *args): def __init__(self, *args):
# Don't share _series and _archives between different Distributions # Don't share _series and _archives between different Distributions
@ -214,7 +215,7 @@ class Distribution(BaseWrapper):
self._series[series.name] = series self._series[series.name] = series
self._series[series.version] = series self._series[series.version] = series
except HTTPError: except HTTPError:
raise SeriesNotFoundException("Error: Release '%s' is unknown in '%s'." % (name_or_version, self.display_name)) raise SeriesNotFoundException("Release '%s' is unknown in '%s'." % (name_or_version, self.display_name))
return self._series[name_or_version] return self._series[name_or_version]
def getDevelopmentSeries(self): def getDevelopmentSeries(self):
@ -233,14 +234,14 @@ class DistroSeries(BaseWrapper):
''' '''
Wrapper class around a LP distro series object. Wrapper class around a LP distro series object.
''' '''
resource_type = lookup_service_root(service) + 'beta/#distro_series' resource_type = lookup_service_root(service) + api_version + '/#distro_series'
class Archive(BaseWrapper): class Archive(BaseWrapper):
''' '''
Wrapper class around a LP archive object. Wrapper class around a LP archive object.
''' '''
resource_type = lookup_service_root(service) + 'beta/#archive' resource_type = lookup_service_root(service) + api_version + '/#archive'
def __init__(self, *args): def __init__(self, *args):
# Don't share _srcpkgs between different Archives # Don't share _srcpkgs between different Archives
@ -260,7 +261,7 @@ class Archive(BaseWrapper):
''' '''
# Check if pocket has a valid value # Check if pocket has a valid value
if pocket not in ('Release', 'Security', 'Updates', 'Proposed', 'Backports'): if pocket not in ('Release', 'Security', 'Updates', 'Proposed', 'Backports'):
raise PocketDoesNotExistException("Pocket '%s' does not exist." % pocket) raise PocketDoesNotExistError("Pocket '%s' does not exist." % pocket)
dist = Distribution(self.distribution_link) dist = Distribution(self.distribution_link)
# Check if series is already a DistoSeries object or not # Check if series is already a DistoSeries object or not
@ -301,7 +302,7 @@ class SourcePackagePublishingHistory(BaseWrapper):
''' '''
Wrapper class around a LP source package object. Wrapper class around a LP source package object.
''' '''
resource_type = lookup_service_root(service) + 'beta/#source_package_publishing_history' resource_type = lookup_service_root(service) + api_version + '/#source_package_publishing_history'
def __init__(self, *args): def __init__(self, *args):
# Don't share _builds between different SourcePackagePublishingHistory objects # Don't share _builds between different SourcePackagePublishingHistory objects
@ -402,8 +403,8 @@ class PersonTeam(BaseWrapper):
__metaclass__ = MetaPersonTeam __metaclass__ = MetaPersonTeam
resource_type = ( resource_type = (
lookup_service_root(service) + 'beta/#person', lookup_service_root(service) + api_version + '/#person',
lookup_service_root(service) + 'beta/#team', lookup_service_root(service) + api_version + '/#team',
) )
def __init__(self, *args): def __init__(self, *args):
@ -493,7 +494,7 @@ class Build(BaseWrapper):
''' '''
Wrapper class around a build object. Wrapper class around a build object.
''' '''
resource_type = lookup_service_root(service) + 'beta/#build' resource_type = lookup_service_root(service) + api_version + '/#build'
def __str__(self): def __str__(self):
return u'%s: %s' % (self.arch_tag, self.buildstate) return u'%s: %s' % (self.arch_tag, self.buildstate)
@ -515,4 +516,4 @@ class DistributionSourcePackage(BaseWrapper):
''' '''
Caching class for distribution_source_package objects. Caching class for distribution_source_package objects.
''' '''
resource_type = lookup_service_root(service) + 'beta/#distribution_source_package' resource_type = lookup_service_root(service) + api_version + '/#distribution_source_package'

View File

@ -6,8 +6,8 @@ class SeriesNotFoundException(BaseException):
""" Thrown when a distroseries is not found """ """ Thrown when a distroseries is not found """
pass pass
class PocketDoesNotExistException(BaseException): class PocketDoesNotExistError(Exception):
""" Thrown when a invalid pocket is passed """ '''Raised when a invalid pocket is used.'''
pass pass
class ArchiveNotFoundException(BaseException): class ArchiveNotFoundException(BaseException):

View File

@ -23,6 +23,8 @@
# Modules. # Modules.
import os import os
from ubuntutools.lp.udtexceptions import PocketDoesNotExistError
def system_distribution(): def system_distribution():
""" system_distro() -> string """ system_distro() -> string
@ -90,3 +92,26 @@ def readlist(filename, uniq=True):
items = list(set(items)) items = list(set(items))
return items return items
def splitReleasePocket(release):
'''Splits the release and pocket name.
If the argument doesn't contain a pocket name then the 'Release' pocket
is assumed.
Returns the release and pocket name.
'''
pocket = 'Release'
if release is None:
raise ValueError('No release name specified')
if '-' in release:
(release, pocket) = release.split('-')
pocket = pocket.capitalize()
if pocket not in ('Release', 'Security', 'Updates', 'Proposed',
'Backports'):
raise PocketDoesNotExistError("Pocket '%s' does not exist." % pocket)
return (release, pocket)

View File

@ -22,7 +22,6 @@
from ubuntutools.requestsync.common import raw_input_exit_on_ctrlc from ubuntutools.requestsync.common import raw_input_exit_on_ctrlc
from ubuntutools.lp.lpapicache import Launchpad, Distribution, PersonTeam, DistributionSourcePackage from ubuntutools.lp.lpapicache import Launchpad, Distribution, PersonTeam, DistributionSourcePackage
from ubuntutools.lp.udtexceptions import PackageNotFoundException, SeriesNotFoundException, PocketDoesNotExistException, ArchiveNotFoundException
from ubuntutools.lp.libsupport import translate_api_web from ubuntutools.lp.libsupport import translate_api_web
def getDebianSrcPkg(name, release): def getDebianSrcPkg(name, release):

View File

@ -180,11 +180,11 @@ def mailBug(srcpkg, subscribe, status, bugtitle, bugtext, keyid = None):
# sign the mail body # sign the mail body
gpg = subprocess.Popen(gpg_command, stdin = subprocess.PIPE, stdout = subprocess.PIPE) gpg = subprocess.Popen(gpg_command, stdin = subprocess.PIPE, stdout = subprocess.PIPE)
signed_report = gpg.communicate(mailbody.encode('utf-8'))[0] signed_report = gpg.communicate(mailbody.encode('utf-8'))[0].decode('utf-8')
assert gpg.returncode == 0 assert gpg.returncode == 0
# generate email # generate email
mail = '''\ mail = u'''\
From: %s From: %s
To: %s To: %s
Subject: %s Subject: %s
@ -223,6 +223,6 @@ Content-Type: text/plain; charset=UTF-8
s.quit() s.quit()
return return
s.sendmail(myemailaddr, to, mail) s.sendmail(myemailaddr, to, mail.encode('utf-8'))
s.quit() s.quit()
print 'Sync request mailed.' print 'Sync request mailed.'