Sync everything from oslo-incubator.

Change-Id: If602b424086abb3edbd4703bc0e3befedbf37925
This commit is contained in:
Russell Bryant
2013-03-22 14:10:07 -04:00
parent e8459ccd59
commit 60c019e235
29 changed files with 115 additions and 64 deletions

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 OpenStack Foundation # Copyright (c) 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -37,9 +37,9 @@ class RequestContext(object):
accesses the system, as well as additional request information. accesses the system, as well as additional request information.
""" """
def __init__(self, auth_tok=None, user=None, tenant=None, is_admin=False, def __init__(self, auth_token=None, user=None, tenant=None, is_admin=False,
read_only=False, show_deleted=False, request_id=None): read_only=False, show_deleted=False, request_id=None):
self.auth_tok = auth_tok self.auth_token = auth_token
self.user = user self.user = user
self.tenant = tenant self.tenant = tenant
self.is_admin = is_admin self.is_admin = is_admin
@@ -55,7 +55,7 @@ class RequestContext(object):
'is_admin': self.is_admin, 'is_admin': self.is_admin,
'read_only': self.read_only, 'read_only': self.read_only,
'show_deleted': self.show_deleted, 'show_deleted': self.show_deleted,
'auth_token': self.auth_tok, 'auth_token': self.auth_token,
'request_id': self.request_id} 'request_id': self.request_id}

View File

@@ -39,8 +39,8 @@ import functools
from oslo.config import cfg from oslo.config import cfg
from nova.openstack.common import lockutils
from nova.openstack.common import importutils from nova.openstack.common import importutils
from nova.openstack.common import lockutils
db_opts = [ db_opts = [

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2012 OpenStack Foundation # Copyright (c) 2012 OpenStack Foundation.
# Administrator of the National Aeronautics and Space Administration. # Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved. # All Rights Reserved.
# #

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# Copyright 2012, Red Hat, Inc. # Copyright 2012, Red Hat, Inc.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -32,6 +32,7 @@ from nova.openstack.common.gettextutils import _
from nova.openstack.common import local from nova.openstack.common import local
from nova.openstack.common import log as logging from nova.openstack.common import log as logging
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@@ -106,10 +107,10 @@ class _InterProcessLock(object):
class _WindowsLock(_InterProcessLock): class _WindowsLock(_InterProcessLock):
def trylock(self): def trylock(self):
msvcrt.locking(self.lockfile, msvcrt.LK_NBLCK, 1) msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_NBLCK, 1)
def unlock(self): def unlock(self):
msvcrt.locking(self.lockfile, msvcrt.LK_UNLCK, 1) msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_UNLCK, 1)
class _PosixLock(_InterProcessLock): class _PosixLock(_InterProcessLock):
@@ -206,7 +207,6 @@ def synchronized(name, lock_file_prefix, external=False, lock_path=None):
local_lock_path = tempfile.mkdtemp() local_lock_path = tempfile.mkdtemp()
if not os.path.exists(local_lock_path): if not os.path.exists(local_lock_path):
cleanup_dir = True
fileutils.ensure_tree(local_lock_path) fileutils.ensure_tree(local_lock_path)
# NOTE(mikal): the lock name cannot contain directory # NOTE(mikal): the lock name cannot contain directory

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# Copyright 2010 United States Government as represented by the # Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration. # Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved. # All Rights Reserved.
@@ -29,6 +29,7 @@ It also allows setting of formatting information through conf.
""" """
import ConfigParser
import cStringIO import cStringIO
import inspect import inspect
import itertools import itertools
@@ -87,11 +88,11 @@ logging_cli_opts = [
metavar='PATH', metavar='PATH',
deprecated_name='logfile', deprecated_name='logfile',
help='(Optional) Name of log file to output to. ' help='(Optional) Name of log file to output to. '
'If not set, logging will go to stdout.'), 'If no default is set, logging will go to stdout.'),
cfg.StrOpt('log-dir', cfg.StrOpt('log-dir',
deprecated_name='logdir', deprecated_name='logdir',
help='(Optional) The directory to keep log files in ' help='(Optional) The base directory used for relative '
'(will be prepended to --log-file)'), '--log-file paths'),
cfg.BoolOpt('use-syslog', cfg.BoolOpt('use-syslog',
default=False, default=False,
help='Use syslog for logging.'), help='Use syslog for logging.'),
@@ -323,12 +324,32 @@ def _create_logging_excepthook(product_name):
return logging_excepthook return logging_excepthook
class LogConfigError(Exception):
message = _('Error loading logging config %(log_config)s: %(err_msg)s')
def __init__(self, log_config, err_msg):
self.log_config = log_config
self.err_msg = err_msg
def __str__(self):
return self.message % dict(log_config=self.log_config,
err_msg=self.err_msg)
def _load_log_config(log_config):
try:
logging.config.fileConfig(log_config)
except ConfigParser.Error, exc:
raise LogConfigError(log_config, str(exc))
def setup(product_name): def setup(product_name):
"""Setup logging.""" """Setup logging."""
if CONF.log_config: if CONF.log_config:
logging.config.fileConfig(CONF.log_config) _load_log_config(CONF.log_config)
else: else:
_setup_logging_from_conf(product_name) _setup_logging_from_conf()
sys.excepthook = _create_logging_excepthook(product_name) sys.excepthook = _create_logging_excepthook(product_name)
@@ -362,8 +383,8 @@ def _find_facility_from_conf():
return facility return facility
def _setup_logging_from_conf(product_name): def _setup_logging_from_conf():
log_root = getLogger(product_name).logger log_root = getLogger(None).logger
for handler in log_root.handlers: for handler in log_root.handlers:
log_root.removeHandler(handler) log_root.removeHandler(handler)
@@ -401,7 +422,8 @@ def _setup_logging_from_conf(product_name):
if CONF.log_format: if CONF.log_format:
handler.setFormatter(logging.Formatter(fmt=CONF.log_format, handler.setFormatter(logging.Formatter(fmt=CONF.log_format,
datefmt=datefmt)) datefmt=datefmt))
handler.setFormatter(LegacyFormatter(datefmt=datefmt)) else:
handler.setFormatter(LegacyFormatter(datefmt=datefmt))
if CONF.debug: if CONF.debug:
log_root.setLevel(logging.DEBUG) log_root.setLevel(logging.DEBUG)

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 OpenStack Foundation # Copyright 2012 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,4 +1,4 @@
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,4 +1,4 @@
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,4 +1,4 @@
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -18,6 +18,7 @@ from oslo.config import cfg
from nova.openstack.common import jsonutils from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging from nova.openstack.common import log as logging
CONF = cfg.CONF CONF = cfg.CONF

View File

@@ -1,4 +1,4 @@
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,4 +1,4 @@
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,4 +1,4 @@
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,4 +1,4 @@
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,4 +1,4 @@
# Copyright 2012 OpenStack Foundation # Copyright 2012 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,4 +1,4 @@
# Copyright 2012 OpenStack Foundation # Copyright 2012 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,4 +1,4 @@
# Copyright 2012 OpenStack Foundation # Copyright 2012 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,4 +1,4 @@
# Copyright 2012 OpenStack Foundation # Copyright 2012 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2012 OpenStack Foundation # Copyright (c) 2012 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 OpenStack Foundation # Copyright (c) 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 OpenStack Foundation # Copyright (c) 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 OpenStack Foundation # Copyright (c) 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# Copyright 2012-2013 Hewlett-Packard Development Company, L.P. # Copyright 2012-2013 Hewlett-Packard Development Company, L.P.
# All Rights Reserved. # All Rights Reserved.
# #
@@ -43,6 +43,11 @@ def parse_mailmap(mailmap='.mailmap'):
return mapping return mapping
def _parse_git_mailmap(git_dir, mailmap='.mailmap'):
mailmap = os.path.join(os.path.dirname(git_dir), mailmap)
return parse_mailmap(mailmap)
def canonicalize_emails(changelog, mapping): def canonicalize_emails(changelog, mapping):
"""Takes in a string and an email alias mapping and replaces all """Takes in a string and an email alias mapping and replaces all
instances of the aliases in the string with their real email. instances of the aliases in the string with their real email.
@@ -117,9 +122,9 @@ def _run_shell_command(cmd, throw_on_error=False):
output = subprocess.Popen(["/bin/sh", "-c", cmd], output = subprocess.Popen(["/bin/sh", "-c", cmd],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) stderr=subprocess.PIPE)
out = output.communicate()
if output.returncode and throw_on_error: if output.returncode and throw_on_error:
raise Exception("%s returned %d" % cmd, output.returncode) raise Exception("%s returned %d" % cmd, output.returncode)
out = output.communicate()
if len(out) == 0: if len(out) == 0:
return None return None
if len(out[0].strip()) == 0: if len(out[0].strip()) == 0:
@@ -127,14 +132,26 @@ def _run_shell_command(cmd, throw_on_error=False):
return out[0].strip() return out[0].strip()
def _get_git_directory():
parent_dir = os.path.dirname(__file__)
while True:
git_dir = os.path.join(parent_dir, '.git')
if os.path.exists(git_dir):
return git_dir
parent_dir, child = os.path.split(parent_dir)
if not child: # reached to root dir
return None
def write_git_changelog(): def write_git_changelog():
"""Write a changelog based on the git changelog.""" """Write a changelog based on the git changelog."""
new_changelog = 'ChangeLog' new_changelog = 'ChangeLog'
git_dir = _get_git_directory()
if not os.getenv('SKIP_WRITE_GIT_CHANGELOG'): if not os.getenv('SKIP_WRITE_GIT_CHANGELOG'):
if os.path.isdir('.git'): if git_dir:
git_log_cmd = 'git log --stat' git_log_cmd = 'git --git-dir=%s log' % git_dir
changelog = _run_shell_command(git_log_cmd) changelog = _run_shell_command(git_log_cmd)
mailmap = parse_mailmap() mailmap = _parse_git_mailmap(git_dir)
with open(new_changelog, "w") as changelog_file: with open(new_changelog, "w") as changelog_file:
changelog_file.write(canonicalize_emails(changelog, mailmap)) changelog_file.write(canonicalize_emails(changelog, mailmap))
else: else:
@@ -146,13 +163,15 @@ def generate_authors():
jenkins_email = 'jenkins@review.(openstack|stackforge).org' jenkins_email = 'jenkins@review.(openstack|stackforge).org'
old_authors = 'AUTHORS.in' old_authors = 'AUTHORS.in'
new_authors = 'AUTHORS' new_authors = 'AUTHORS'
git_dir = _get_git_directory()
if not os.getenv('SKIP_GENERATE_AUTHORS'): if not os.getenv('SKIP_GENERATE_AUTHORS'):
if os.path.isdir('.git'): if git_dir:
# don't include jenkins email address in AUTHORS file # don't include jenkins email address in AUTHORS file
git_log_cmd = ("git log --format='%aN <%aE>' | sort -u | " git_log_cmd = ("git --git-dir=" + git_dir +
" log --format='%aN <%aE>' | sort -u | "
"egrep -v '" + jenkins_email + "'") "egrep -v '" + jenkins_email + "'")
changelog = _run_shell_command(git_log_cmd) changelog = _run_shell_command(git_log_cmd)
mailmap = parse_mailmap() mailmap = _parse_git_mailmap(git_dir)
with open(new_authors, 'w') as new_authors_fh: with open(new_authors, 'w') as new_authors_fh:
new_authors_fh.write(canonicalize_emails(changelog, mailmap)) new_authors_fh.write(canonicalize_emails(changelog, mailmap))
if os.path.exists(old_authors): if os.path.exists(old_authors):
@@ -258,19 +277,21 @@ def get_cmdclass():
return cmdclass return cmdclass
def _get_revno(): def _get_revno(git_dir):
"""Return the number of commits since the most recent tag. """Return the number of commits since the most recent tag.
We use git-describe to find this out, but if there are no We use git-describe to find this out, but if there are no
tags then we fall back to counting commits since the beginning tags then we fall back to counting commits since the beginning
of time. of time.
""" """
describe = _run_shell_command("git describe --always") describe = _run_shell_command(
"git --git-dir=%s describe --always" % git_dir)
if "-" in describe: if "-" in describe:
return describe.rsplit("-", 2)[-2] return describe.rsplit("-", 2)[-2]
# no tags found # no tags found
revlist = _run_shell_command("git rev-list --abbrev-commit HEAD") revlist = _run_shell_command(
"git --git-dir=%s rev-list --abbrev-commit HEAD" % git_dir)
return len(revlist.splitlines()) return len(revlist.splitlines())
@@ -279,18 +300,21 @@ def _get_version_from_git(pre_version):
revision if there is one, or tag plus number of additional revisions revision if there is one, or tag plus number of additional revisions
if the current revision has no tag.""" if the current revision has no tag."""
if os.path.isdir('.git'): git_dir = _get_git_directory()
if git_dir:
if pre_version: if pre_version:
try: try:
return _run_shell_command( return _run_shell_command(
"git describe --exact-match", "git --git-dir=" + git_dir + " describe --exact-match",
throw_on_error=True).replace('-', '.') throw_on_error=True).replace('-', '.')
except Exception: except Exception:
sha = _run_shell_command("git log -n1 --pretty=format:%h") sha = _run_shell_command(
return "%s.a%s.g%s" % (pre_version, _get_revno(), sha) "git --git-dir=" + git_dir + " log -n1 --pretty=format:%h")
return "%s.a%s.g%s" % (pre_version, _get_revno(git_dir), sha)
else: else:
return _run_shell_command( return _run_shell_command(
"git describe --always").replace('-', '.') "git --git-dir=" + git_dir + " describe --always").replace(
'-', '.')
return None return None

View File

@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4 # vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack Foundation # Copyright 2011 OpenStack Foundation.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -25,18 +25,22 @@ import datetime
import iso8601 import iso8601
TIME_FORMAT = "%Y-%m-%dT%H:%M:%S" # ISO 8601 extended time format with microseconds
PERFECT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f" _ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f'
_ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
PERFECT_TIME_FORMAT = _ISO8601_TIME_FORMAT_SUBSECOND
def isotime(at=None): def isotime(at=None, subsecond=False):
"""Stringify time in ISO 8601 format""" """Stringify time in ISO 8601 format"""
if not at: if not at:
at = utcnow() at = utcnow()
str = at.strftime(TIME_FORMAT) st = at.strftime(_ISO8601_TIME_FORMAT
if not subsecond
else _ISO8601_TIME_FORMAT_SUBSECOND)
tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC' tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC'
str += ('Z' if tz == 'UTC' else tz) st += ('Z' if tz == 'UTC' else tz)
return str return st
def parse_isotime(timestr): def parse_isotime(timestr):