Add timezone option to config
Using dateutil.tz to link string names to tzinfo objects, the create_date can now generate using a named timezone rather than datetime.now(). Change-Id: I9f151cb9e11da3d68be63d7141f60e7eccb9812c Fixes: #425
This commit is contained in:
parent
4cdb25bf5d
commit
fbbb669b8c
|
@ -1,4 +1,5 @@
|
|||
import datetime
|
||||
from dateutil import tz
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
|
@ -42,7 +43,8 @@ class ScriptDirectory(object):
|
|||
def __init__(self, dir, file_template=_default_file_template,
|
||||
truncate_slug_length=40,
|
||||
version_locations=None,
|
||||
sourceless=False, output_encoding="utf-8"):
|
||||
sourceless=False, output_encoding="utf-8",
|
||||
timezone=None):
|
||||
self.dir = dir
|
||||
self.file_template = file_template
|
||||
self.version_locations = version_locations
|
||||
|
@ -50,6 +52,7 @@ class ScriptDirectory(object):
|
|||
self.sourceless = sourceless
|
||||
self.output_encoding = output_encoding
|
||||
self.revision_map = revision.RevisionMap(self._load_revisions)
|
||||
self.timezone = timezone
|
||||
|
||||
if not os.access(dir, os.F_OK):
|
||||
raise util.CommandError("Path doesn't exist: %r. Please use "
|
||||
|
@ -118,6 +121,7 @@ class ScriptDirectory(object):
|
|||
version_locations = config.get_main_option("version_locations")
|
||||
if version_locations:
|
||||
version_locations = _split_on_space_comma.split(version_locations)
|
||||
|
||||
return ScriptDirectory(
|
||||
util.coerce_resource_to_filename(script_location),
|
||||
file_template=config.get_main_option(
|
||||
|
@ -126,7 +130,8 @@ class ScriptDirectory(object):
|
|||
truncate_slug_length=truncate_slug_length,
|
||||
sourceless=config.get_main_option("sourceless") == "true",
|
||||
output_encoding=config.get_main_option("output_encoding", "utf-8"),
|
||||
version_locations=version_locations
|
||||
version_locations=version_locations,
|
||||
timezone=config.get_main_option("timezone")
|
||||
)
|
||||
|
||||
@contextmanager
|
||||
|
@ -440,6 +445,18 @@ class ScriptDirectory(object):
|
|||
"Creating directory %s" % path,
|
||||
os.makedirs, path)
|
||||
|
||||
def _generate_create_date(self):
|
||||
if self.timezone is not None:
|
||||
tzinfo = tz.gettz(self.timezone.upper())
|
||||
if tzinfo is None:
|
||||
raise util.CommandError(
|
||||
"Can't locate timezone: %s" % self.timezone)
|
||||
create_date = datetime.datetime.utcnow().replace(
|
||||
tzinfo=tz.tzutc()).astimezone(tzinfo)
|
||||
else:
|
||||
create_date = datetime.datetime.now()
|
||||
return create_date
|
||||
|
||||
def generate_revision(
|
||||
self, revid, message, head=None,
|
||||
refresh=False, splice=False, branch_labels=None,
|
||||
|
@ -478,7 +495,7 @@ class ScriptDirectory(object):
|
|||
if len(set(heads)) != len(heads):
|
||||
raise util.CommandError("Duplicate head revisions specified")
|
||||
|
||||
create_date = datetime.datetime.now()
|
||||
create_date = self._generate_create_date()
|
||||
|
||||
if version_path is None:
|
||||
if len(self._version_locations) > 1:
|
||||
|
|
|
@ -7,6 +7,12 @@ script_location = ${script_location}
|
|||
# template used to generate migration files
|
||||
# file_template = %%(rev)s_%%(slug)s
|
||||
|
||||
# timezone to use when rendering the date
|
||||
# within the migration file as well as the filename.
|
||||
# string value is passed to dateutil.tz.gettz()
|
||||
# leave blank for localtime
|
||||
# timezone =
|
||||
|
||||
# max length of characters to apply to the
|
||||
# "slug" field
|
||||
#truncate_slug_length = 40
|
||||
|
|
|
@ -7,6 +7,12 @@ script_location = ${script_location}
|
|||
# template used to generate migration files
|
||||
# file_template = %%(rev)s_%%(slug)s
|
||||
|
||||
# timezone to use when rendering the date
|
||||
# within the migration file as well as the filename.
|
||||
# string value is passed to dateutil.tz.gettz()
|
||||
# leave blank for localtime
|
||||
# timezone =
|
||||
|
||||
# max length of characters to apply to the
|
||||
# "slug" field
|
||||
#truncate_slug_length = 40
|
||||
|
|
|
@ -7,6 +7,12 @@ script_location = ${script_location}
|
|||
# template used to generate migration files
|
||||
# file_template = %%(rev)s_%%(slug)s
|
||||
|
||||
# timezone to use when rendering the date
|
||||
# within the migration file as well as the filename.
|
||||
# string value is passed to dateutil.tz.gettz()
|
||||
# leave blank for localtime
|
||||
# timezone =
|
||||
|
||||
# max length of characters to apply to the
|
||||
# "slug" field
|
||||
#truncate_slug_length = 40
|
||||
|
|
|
@ -7,6 +7,16 @@ Changelog
|
|||
:version: 0.9.2
|
||||
:released:
|
||||
|
||||
.. change:: 425
|
||||
:tags: feature, commands
|
||||
:tickets: 425
|
||||
|
||||
Added a new configuration option ``timezone``, a string timezone name
|
||||
that will be applied to the create date timestamp rendered
|
||||
inside the revision file as made availble to the ``file_template`` used
|
||||
to generate the revision filename. Note this change adds the
|
||||
``python-dateutil`` package as a dependency.
|
||||
|
||||
.. change:: 421
|
||||
:tags: bug, autogenerate
|
||||
:tickets: 421
|
||||
|
|
|
@ -119,6 +119,12 @@ The file generated with the "generic" configuration looks like::
|
|||
# template used to generate migration files
|
||||
# file_template = %%(rev)s_%%(slug)s
|
||||
|
||||
# timezone to use when rendering the date
|
||||
# within the migration file as well as the filename.
|
||||
# string value is passed to dateutil.tz.gettz()
|
||||
# leave blank for localtime
|
||||
# timezone =
|
||||
|
||||
# max length of characters to apply to the
|
||||
# "slug" field
|
||||
#truncate_slug_length = 40
|
||||
|
@ -212,8 +218,23 @@ This file contains the following features:
|
|||
* ``%%(rev)s`` - revision id
|
||||
* ``%%(slug)s`` - a truncated string derived from the revision message
|
||||
* ``%%(year)d``, ``%%(month).2d``, ``%%(day).2d``, ``%%(hour).2d``,
|
||||
``%%(minute).2d``, ``%%(second).2d`` - components of the create date
|
||||
as returned by ``datetime.datetime.now()``
|
||||
``%%(minute).2d``, ``%%(second).2d`` - components of the create date,
|
||||
by default ``datetime.datetime.now()`` unless the ``timezone``
|
||||
configuration option is also used.
|
||||
|
||||
* ``timezone`` - an optional timezone name (e.g. ``UTC``, ``EST5EDT``, etc.)
|
||||
that will be applied to the timestamp which renders inside the migration
|
||||
file's comment as well as within the filename. If ``timezone`` is specified,
|
||||
the create date object is no longer derived from ``datetime.datetime.now()``
|
||||
and is instead generated as::
|
||||
|
||||
datetime.datetime.utcnow().replace(
|
||||
tzinfo=dateutil.tz.tzutc()
|
||||
).astimezone(
|
||||
dateutil.tz.gettz(<timezone>)
|
||||
)
|
||||
|
||||
.. versionadded:: 0.9.2
|
||||
|
||||
* ``truncate_slug_length`` - defaults to 40, the max number of characters
|
||||
to include in the "slug" field.
|
||||
|
|
1
setup.py
1
setup.py
|
@ -16,6 +16,7 @@ requires = [
|
|||
'SQLAlchemy>=0.7.6',
|
||||
'Mako',
|
||||
'python-editor>=0.3',
|
||||
'python-dateutil'
|
||||
]
|
||||
|
||||
try:
|
||||
|
|
|
@ -18,6 +18,7 @@ import sqlalchemy as sa
|
|||
from sqlalchemy.engine.reflection import Inspector
|
||||
from alembic.util import CommandError
|
||||
import re
|
||||
from dateutil import tz
|
||||
|
||||
env, abc, def_ = None, None, None
|
||||
|
||||
|
@ -153,6 +154,72 @@ class ScriptNamingTest(TestBase):
|
|||
"message_2012_7_25_15_8_5.py" % _get_staging_directory())
|
||||
)
|
||||
|
||||
def _test_tz(self, timezone_arg, given, expected):
|
||||
script = ScriptDirectory(
|
||||
_get_staging_directory(),
|
||||
file_template="%(rev)s_%(slug)s_"
|
||||
"%(year)s_%(month)s_"
|
||||
"%(day)s_%(hour)s_"
|
||||
"%(minute)s_%(second)s",
|
||||
timezone=timezone_arg
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"alembic.script.base.datetime",
|
||||
mock.Mock(
|
||||
datetime=mock.Mock(
|
||||
utcnow=lambda: given,
|
||||
now=lambda: given
|
||||
)
|
||||
)
|
||||
):
|
||||
create_date = script._generate_create_date()
|
||||
eq_(
|
||||
create_date,
|
||||
expected
|
||||
)
|
||||
|
||||
def test_custom_tz(self):
|
||||
self._test_tz(
|
||||
'EST5EDT',
|
||||
datetime.datetime(2012, 7, 25, 15, 8, 5),
|
||||
datetime.datetime(
|
||||
2012, 7, 25, 11, 8, 5, tzinfo=tz.gettz('EST5EDT'))
|
||||
)
|
||||
|
||||
def test_custom_tz_lowercase(self):
|
||||
self._test_tz(
|
||||
'est5edt',
|
||||
datetime.datetime(2012, 7, 25, 15, 8, 5),
|
||||
datetime.datetime(
|
||||
2012, 7, 25, 11, 8, 5, tzinfo=tz.gettz('EST5EDT'))
|
||||
)
|
||||
|
||||
def test_custom_tz_utc(self):
|
||||
self._test_tz(
|
||||
'utc',
|
||||
datetime.datetime(2012, 7, 25, 15, 8, 5),
|
||||
datetime.datetime(
|
||||
2012, 7, 25, 15, 8, 5, tzinfo=tz.gettz('UTC'))
|
||||
)
|
||||
|
||||
def test_default_tz(self):
|
||||
self._test_tz(
|
||||
None,
|
||||
datetime.datetime(2012, 7, 25, 15, 8, 5),
|
||||
datetime.datetime(2012, 7, 25, 15, 8, 5)
|
||||
)
|
||||
|
||||
def test_tz_cant_locate(self):
|
||||
assert_raises_message(
|
||||
CommandError,
|
||||
"Can't locate timezone: fake",
|
||||
self._test_tz,
|
||||
"fake",
|
||||
datetime.datetime(2012, 7, 25, 15, 8, 5),
|
||||
datetime.datetime(2012, 7, 25, 15, 8, 5)
|
||||
)
|
||||
|
||||
|
||||
class RevisionCommandTest(TestBase):
|
||||
def setUp(self):
|
||||
|
|
Loading…
Reference in New Issue