gcloud 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data.tar.gz.sig +2 -3
- data/CHANGELOG +4 -0
- data/LICENSE +674 -0
- data/Manifest +111 -0
- data/README.md +4 -3
- data/bin/gcutil +53 -0
- data/gcloud.gemspec +4 -3
- data/packages/gcutil-1.7.1/CHANGELOG +197 -0
- data/packages/gcutil-1.7.1/LICENSE +202 -0
- data/packages/gcutil-1.7.1/VERSION +1 -0
- data/packages/gcutil-1.7.1/gcutil +53 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/LICENSE +23 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/__init__.py +1 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/discovery.py +743 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/errors.py +123 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/ext/__init__.py +0 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/http.py +1443 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/mimeparse.py +172 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/model.py +385 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/schema.py +303 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/__init__.py +1 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/anyjson.py +32 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/appengine.py +528 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.py +1139 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/clientsecrets.py +105 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/crypt.py +244 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/django_orm.py +124 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/file.py +107 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/locked_file.py +343 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/multistore_file.py +379 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/tools.py +174 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/uritemplate/__init__.py +147 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/LICENSE +202 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/__init__.py +3 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/__init__.py +3 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/app.py +356 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/appcommands.py +783 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/basetest.py +1260 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/datelib.py +421 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/debug.py +60 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/file_util.py +181 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/resources.py +67 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/run_script_module.py +217 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/setup_command.py +159 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/shellutil.py +49 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/stopwatch.py +204 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/__init__.py +0 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auth_helper.py +140 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auth_helper_test.py +149 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auto_auth.py +130 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auto_auth_test.py +75 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/basic_cmds.py +128 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/basic_cmds_test.py +111 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/command_base.py +1808 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/command_base_test.py +1651 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/compute/v1beta13.json +2851 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/compute/v1beta14.json +3361 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/disk_cmds.py +342 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/disk_cmds_test.py +474 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/firewall_cmds.py +344 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/firewall_cmds_test.py +231 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/flags_cache.py +274 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/gcutil +89 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/gcutil_logging.py +69 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/image_cmds.py +262 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/image_cmds_test.py +172 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/instance_cmds.py +1506 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/instance_cmds_test.py +1904 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/kernel_cmds.py +91 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/kernel_cmds_test.py +56 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/machine_type_cmds.py +106 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/machine_type_cmds_test.py +59 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata.py +96 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata_lib.py +357 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata_test.py +84 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/mock_api.py +420 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/mock_metadata.py +58 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/move_cmds.py +824 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/move_cmds_test.py +307 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/network_cmds.py +178 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/network_cmds_test.py +133 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/operation_cmds.py +181 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/operation_cmds_test.py +196 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/path_initializer.py +38 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/project_cmds.py +173 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/project_cmds_test.py +111 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/scopes.py +61 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/scopes_test.py +50 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/snapshot_cmds.py +276 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/snapshot_cmds_test.py +260 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/ssh_keys.py +266 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/ssh_keys_test.py +128 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/table_formatter.py +563 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/thread_pool.py +188 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/thread_pool_test.py +88 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/utils.py +208 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/utils_test.py +193 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version.py +17 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version_checker.py +246 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version_checker_test.py +271 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/zone_cmds.py +151 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/zone_cmds_test.py +60 -0
- data/packages/gcutil-1.7.1/lib/httplib2/LICENSE +21 -0
- data/packages/gcutil-1.7.1/lib/httplib2/httplib2/__init__.py +1630 -0
- data/packages/gcutil-1.7.1/lib/httplib2/httplib2/cacerts.txt +714 -0
- data/packages/gcutil-1.7.1/lib/httplib2/httplib2/iri2uri.py +110 -0
- data/packages/gcutil-1.7.1/lib/httplib2/httplib2/socks.py +438 -0
- data/packages/gcutil-1.7.1/lib/iso8601/LICENSE +20 -0
- data/packages/gcutil-1.7.1/lib/iso8601/iso8601/__init__.py +1 -0
- data/packages/gcutil-1.7.1/lib/iso8601/iso8601/iso8601.py +102 -0
- data/packages/gcutil-1.7.1/lib/iso8601/iso8601/test_iso8601.py +111 -0
- data/packages/gcutil-1.7.1/lib/python_gflags/AUTHORS +2 -0
- data/packages/gcutil-1.7.1/lib/python_gflags/LICENSE +28 -0
- data/packages/gcutil-1.7.1/lib/python_gflags/gflags.py +2862 -0
- data/packages/gcutil-1.7.1/lib/python_gflags/gflags2man.py +544 -0
- data/packages/gcutil-1.7.1/lib/python_gflags/gflags_validators.py +187 -0
- metadata +118 -5
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# Copyright 2002 Google Inc. All Rights Reserved.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS-IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
"""Set of classes and functions for dealing with dates and timestamps.
|
|
17
|
+
|
|
18
|
+
The BaseTimestamp and Timestamp are timezone-aware wrappers around Python
|
|
19
|
+
datetime.datetime class.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
import calendar
|
|
25
|
+
import copy
|
|
26
|
+
import datetime
|
|
27
|
+
import re
|
|
28
|
+
import sys
|
|
29
|
+
import time
|
|
30
|
+
import types
|
|
31
|
+
|
|
32
|
+
from dateutil import parser
|
|
33
|
+
import pytz
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
_MICROSECONDS_PER_SECOND = 1000000
|
|
37
|
+
_MICROSECONDS_PER_SECOND_F = float(_MICROSECONDS_PER_SECOND)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def SecondsToMicroseconds(seconds):
|
|
41
|
+
"""Convert seconds to microseconds.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
seconds: number
|
|
45
|
+
Returns:
|
|
46
|
+
microseconds
|
|
47
|
+
"""
|
|
48
|
+
return seconds * _MICROSECONDS_PER_SECOND
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _GetCurrentTimeMicros():
|
|
52
|
+
"""Get the current time in microseconds, in UTC.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
The number of microseconds since the epoch.
|
|
56
|
+
"""
|
|
57
|
+
return int(SecondsToMicroseconds(time.time()))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def GetSecondsSinceEpoch(time_tuple):
|
|
61
|
+
"""Convert time_tuple (in UTC) to seconds (also in UTC).
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
time_tuple: tuple with at least 6 items.
|
|
65
|
+
Returns:
|
|
66
|
+
seconds.
|
|
67
|
+
"""
|
|
68
|
+
return calendar.timegm(time_tuple[:6] + (0, 0, 0))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def GetTimeMicros(time_tuple):
|
|
72
|
+
"""Get a time in microseconds.
|
|
73
|
+
|
|
74
|
+
Arguments:
|
|
75
|
+
time_tuple: A (year, month, day, hour, minute, second) tuple (the python
|
|
76
|
+
time tuple format) in the UTC time zone.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
The number of microseconds since the epoch represented by the input tuple.
|
|
80
|
+
"""
|
|
81
|
+
return int(SecondsToMicroseconds(GetSecondsSinceEpoch(time_tuple)))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
UTC = pytz.UTC
|
|
85
|
+
US_PACIFIC = pytz.timezone('US/Pacific')
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class TimestampError(ValueError):
|
|
89
|
+
"""Generic timestamp-related error."""
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class TimezoneNotSpecifiedError(TimestampError):
|
|
94
|
+
"""This error is raised when timezone is not specified."""
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class TimeParseError(TimestampError):
|
|
99
|
+
"""This error is raised when we can't parse the input."""
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# TODO(user): this class needs to handle daylight better
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class LocalTimezoneClass(datetime.tzinfo):
|
|
107
|
+
"""This class defines local timezone."""
|
|
108
|
+
|
|
109
|
+
ZERO = datetime.timedelta(0)
|
|
110
|
+
HOUR = datetime.timedelta(hours=1)
|
|
111
|
+
|
|
112
|
+
STDOFFSET = datetime.timedelta(seconds=-time.timezone)
|
|
113
|
+
if time.daylight:
|
|
114
|
+
DSTOFFSET = datetime.timedelta(seconds=-time.altzone)
|
|
115
|
+
else:
|
|
116
|
+
DSTOFFSET = STDOFFSET
|
|
117
|
+
|
|
118
|
+
DSTDIFF = DSTOFFSET - STDOFFSET
|
|
119
|
+
|
|
120
|
+
def utcoffset(self, dt):
|
|
121
|
+
"""datetime -> minutes east of UTC (negative for west of UTC)."""
|
|
122
|
+
if self._isdst(dt):
|
|
123
|
+
return self.DSTOFFSET
|
|
124
|
+
else:
|
|
125
|
+
return self.STDOFFSET
|
|
126
|
+
|
|
127
|
+
def dst(self, dt):
|
|
128
|
+
"""datetime -> DST offset in minutes east of UTC."""
|
|
129
|
+
if self._isdst(dt):
|
|
130
|
+
return self.DSTDIFF
|
|
131
|
+
else:
|
|
132
|
+
return self.ZERO
|
|
133
|
+
|
|
134
|
+
def tzname(self, dt):
|
|
135
|
+
"""datetime -> string name of time zone."""
|
|
136
|
+
return time.tzname[self._isdst(dt)]
|
|
137
|
+
|
|
138
|
+
def _isdst(self, dt):
|
|
139
|
+
"""Return true if given datetime is within local DST."""
|
|
140
|
+
tt = (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
|
|
141
|
+
dt.weekday(), 0, -1)
|
|
142
|
+
stamp = time.mktime(tt)
|
|
143
|
+
tt = time.localtime(stamp)
|
|
144
|
+
return tt.tm_isdst > 0
|
|
145
|
+
|
|
146
|
+
def __repr__(self):
|
|
147
|
+
"""Return string '<Local>'."""
|
|
148
|
+
return '<Local>'
|
|
149
|
+
|
|
150
|
+
def localize(self, dt, unused_is_dst=False):
|
|
151
|
+
"""Convert naive time to local time."""
|
|
152
|
+
if dt.tzinfo is not None:
|
|
153
|
+
raise ValueError('Not naive datetime (tzinfo is already set)')
|
|
154
|
+
return dt.replace(tzinfo=self)
|
|
155
|
+
|
|
156
|
+
def normalize(self, dt, unused_is_dst=False):
|
|
157
|
+
"""Correct the timezone information on the given datetime."""
|
|
158
|
+
if dt.tzinfo is None:
|
|
159
|
+
raise ValueError('Naive time - no tzinfo set')
|
|
160
|
+
return dt.replace(tzinfo=self)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
LocalTimezone = LocalTimezoneClass()
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class BaseTimestamp(datetime.datetime):
|
|
167
|
+
"""Our kind of wrapper over datetime.datetime.
|
|
168
|
+
|
|
169
|
+
The objects produced by methods now, today, fromtimestamp, utcnow,
|
|
170
|
+
utcfromtimestamp are timezone-aware (with correct timezone).
|
|
171
|
+
We also overload __add__ and __sub__ method, to fix the result of arithmetic
|
|
172
|
+
operations.
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
LocalTimezone = LocalTimezone
|
|
176
|
+
|
|
177
|
+
@classmethod
|
|
178
|
+
def AddLocalTimezone(cls, obj):
|
|
179
|
+
"""If obj is naive, add local timezone to it."""
|
|
180
|
+
if not obj.tzinfo:
|
|
181
|
+
return obj.replace(tzinfo=cls.LocalTimezone)
|
|
182
|
+
return obj
|
|
183
|
+
|
|
184
|
+
@classmethod
|
|
185
|
+
def Localize(cls, obj):
|
|
186
|
+
"""If obj is naive, localize it to cls.LocalTimezone."""
|
|
187
|
+
if not obj.tzinfo:
|
|
188
|
+
return cls.LocalTimezone.localize(obj)
|
|
189
|
+
return obj
|
|
190
|
+
|
|
191
|
+
def __add__(self, *args, **kwargs):
|
|
192
|
+
"""x.__add__(y) <==> x+y."""
|
|
193
|
+
r = super(BaseTimestamp, self).__add__(*args, **kwargs)
|
|
194
|
+
return type(self)(r.year, r.month, r.day, r.hour, r.minute, r.second,
|
|
195
|
+
r.microsecond, r.tzinfo)
|
|
196
|
+
|
|
197
|
+
def __sub__(self, *args, **kwargs):
|
|
198
|
+
"""x.__add__(y) <==> x-y."""
|
|
199
|
+
r = super(BaseTimestamp, self).__sub__(*args, **kwargs)
|
|
200
|
+
if isinstance(r, datetime.datetime):
|
|
201
|
+
return type(self)(r.year, r.month, r.day, r.hour, r.minute, r.second,
|
|
202
|
+
r.microsecond, r.tzinfo)
|
|
203
|
+
return r
|
|
204
|
+
|
|
205
|
+
@classmethod
|
|
206
|
+
def now(cls, *args, **kwargs):
|
|
207
|
+
"""Get a timestamp corresponding to right now.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
args: Positional arguments to pass to datetime.datetime.now().
|
|
211
|
+
kwargs: Keyword arguments to pass to datetime.datetime.now(). If tz is not
|
|
212
|
+
specified, local timezone is assumed.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
A new BaseTimestamp with tz's local day and time.
|
|
216
|
+
"""
|
|
217
|
+
return cls.AddLocalTimezone(
|
|
218
|
+
super(BaseTimestamp, cls).now(*args, **kwargs))
|
|
219
|
+
|
|
220
|
+
@classmethod
|
|
221
|
+
def today(cls):
|
|
222
|
+
"""Current BaseTimestamp.
|
|
223
|
+
|
|
224
|
+
Same as self.__class__.fromtimestamp(time.time()).
|
|
225
|
+
Returns:
|
|
226
|
+
New self.__class__.
|
|
227
|
+
"""
|
|
228
|
+
return cls.AddLocalTimezone(super(BaseTimestamp, cls).today())
|
|
229
|
+
|
|
230
|
+
@classmethod
|
|
231
|
+
def fromtimestamp(cls, *args, **kwargs):
|
|
232
|
+
"""Get a new localized timestamp from a POSIX timestamp.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
args: Positional arguments to pass to datetime.datetime.fromtimestamp().
|
|
236
|
+
kwargs: Keyword arguments to pass to datetime.datetime.fromtimestamp().
|
|
237
|
+
If tz is not specified, local timezone is assumed.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
A new BaseTimestamp with tz's local day and time.
|
|
241
|
+
"""
|
|
242
|
+
return cls.Localize(
|
|
243
|
+
super(BaseTimestamp, cls).fromtimestamp(*args, **kwargs))
|
|
244
|
+
|
|
245
|
+
@classmethod
|
|
246
|
+
def utcnow(cls):
|
|
247
|
+
"""Return a new BaseTimestamp representing UTC day and time."""
|
|
248
|
+
return super(BaseTimestamp, cls).utcnow().replace(tzinfo=pytz.utc)
|
|
249
|
+
|
|
250
|
+
@classmethod
|
|
251
|
+
def utcfromtimestamp(cls, *args, **kwargs):
|
|
252
|
+
"""timestamp -> UTC datetime from a POSIX timestamp (like time.time())."""
|
|
253
|
+
return super(BaseTimestamp, cls).utcfromtimestamp(
|
|
254
|
+
*args, **kwargs).replace(tzinfo=pytz.utc)
|
|
255
|
+
|
|
256
|
+
@classmethod
|
|
257
|
+
def strptime(cls, date_string, format, tz=None):
|
|
258
|
+
"""Parse date_string according to format and construct BaseTimestamp.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
date_string: string passed to time.strptime.
|
|
262
|
+
format: format string passed to time.strptime.
|
|
263
|
+
tz: if not specified, local timezone assumed.
|
|
264
|
+
Returns:
|
|
265
|
+
New BaseTimestamp.
|
|
266
|
+
"""
|
|
267
|
+
if tz is None:
|
|
268
|
+
return cls.Localize(cls(*(time.strptime(date_string, format)[:6])))
|
|
269
|
+
return tz.localize(cls(*(time.strptime(date_string, format)[:6])))
|
|
270
|
+
|
|
271
|
+
def astimezone(self, *args, **kwargs):
|
|
272
|
+
"""tz -> convert to time in new timezone tz."""
|
|
273
|
+
r = super(BaseTimestamp, self).astimezone(*args, **kwargs)
|
|
274
|
+
return type(self)(r.year, r.month, r.day, r.hour, r.minute, r.second,
|
|
275
|
+
r.microsecond, r.tzinfo)
|
|
276
|
+
|
|
277
|
+
@classmethod
|
|
278
|
+
def FromMicroTimestamp(cls, ts):
|
|
279
|
+
"""Create new Timestamp object from microsecond UTC timestamp value.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
ts: integer microsecond UTC timestamp
|
|
283
|
+
Returns:
|
|
284
|
+
New cls()
|
|
285
|
+
"""
|
|
286
|
+
return cls.utcfromtimestamp(ts/_MICROSECONDS_PER_SECOND_F)
|
|
287
|
+
|
|
288
|
+
def AsSecondsSinceEpoch(self):
|
|
289
|
+
"""Return number of seconds since epoch (timestamp in seconds)."""
|
|
290
|
+
return GetSecondsSinceEpoch(self.utctimetuple())
|
|
291
|
+
|
|
292
|
+
def AsMicroTimestamp(self):
|
|
293
|
+
"""Return microsecond timestamp constructed from this object."""
|
|
294
|
+
return (SecondsToMicroseconds(self.AsSecondsSinceEpoch()) +
|
|
295
|
+
self.microsecond)
|
|
296
|
+
|
|
297
|
+
@classmethod
|
|
298
|
+
def combine(cls, datepart, timepart, tz=None):
|
|
299
|
+
"""Combine date and time into timestamp, timezone-aware.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
datepart: datetime.date
|
|
303
|
+
timepart: datetime.time
|
|
304
|
+
tz: timezone or None
|
|
305
|
+
Returns:
|
|
306
|
+
timestamp object
|
|
307
|
+
"""
|
|
308
|
+
result = super(BaseTimestamp, cls).combine(datepart, timepart)
|
|
309
|
+
if tz:
|
|
310
|
+
result = tz.localize(result)
|
|
311
|
+
return result
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
# Conversions from interval suffixes to number of seconds.
|
|
315
|
+
# (m => 60s, d => 86400s, etc)
|
|
316
|
+
_INTERVAL_CONV_DICT = {'s': 1}
|
|
317
|
+
_INTERVAL_CONV_DICT['m'] = 60 * _INTERVAL_CONV_DICT['s']
|
|
318
|
+
_INTERVAL_CONV_DICT['h'] = 60 * _INTERVAL_CONV_DICT['m']
|
|
319
|
+
_INTERVAL_CONV_DICT['d'] = 24 * _INTERVAL_CONV_DICT['h']
|
|
320
|
+
_INTERVAL_CONV_DICT['D'] = _INTERVAL_CONV_DICT['d']
|
|
321
|
+
_INTERVAL_CONV_DICT['w'] = 7 * _INTERVAL_CONV_DICT['d']
|
|
322
|
+
_INTERVAL_CONV_DICT['W'] = _INTERVAL_CONV_DICT['w']
|
|
323
|
+
_INTERVAL_CONV_DICT['M'] = 30 * _INTERVAL_CONV_DICT['d']
|
|
324
|
+
_INTERVAL_CONV_DICT['Y'] = 365 * _INTERVAL_CONV_DICT['d']
|
|
325
|
+
_INTERVAL_REGEXP = re.compile('^([0-9]+)([%s])?' % ''.join(_INTERVAL_CONV_DICT))
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def ConvertIntervalToSeconds(interval):
|
|
329
|
+
"""Convert a formatted string representing an interval into seconds.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
interval: String to interpret as an interval. A basic interval looks like
|
|
333
|
+
"<number><suffix>". Complex intervals consisting of a chain of basic
|
|
334
|
+
intervals are also allowed.
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
An integer representing the number of seconds represented by the interval
|
|
338
|
+
string, or None if the interval string could not be decoded.
|
|
339
|
+
"""
|
|
340
|
+
total = 0
|
|
341
|
+
while interval:
|
|
342
|
+
match = _INTERVAL_REGEXP.match(interval)
|
|
343
|
+
if not match:
|
|
344
|
+
return None
|
|
345
|
+
|
|
346
|
+
try:
|
|
347
|
+
num = int(match.group(1))
|
|
348
|
+
except ValueError:
|
|
349
|
+
return None
|
|
350
|
+
|
|
351
|
+
suffix = match.group(2)
|
|
352
|
+
if suffix:
|
|
353
|
+
multiplier = _INTERVAL_CONV_DICT.get(suffix)
|
|
354
|
+
if not multiplier:
|
|
355
|
+
return None
|
|
356
|
+
num *= multiplier
|
|
357
|
+
|
|
358
|
+
total += num
|
|
359
|
+
interval = interval[match.end(0):]
|
|
360
|
+
return total
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class Timestamp(BaseTimestamp):
|
|
364
|
+
"""This subclass contains methods to parse W3C and interval date spec.
|
|
365
|
+
|
|
366
|
+
The interval date specification is in the form "1D", where "D" can be
|
|
367
|
+
"s"econds "m"inutes "h"ours "D"ays "W"eeks "M"onths "Y"ears.
|
|
368
|
+
"""
|
|
369
|
+
INTERVAL_CONV_DICT = _INTERVAL_CONV_DICT
|
|
370
|
+
INTERVAL_REGEXP = _INTERVAL_REGEXP
|
|
371
|
+
|
|
372
|
+
@classmethod
|
|
373
|
+
def _StringToTime(cls, timestring, tz=None):
|
|
374
|
+
"""Use dateutil.parser to convert string into timestamp.
|
|
375
|
+
|
|
376
|
+
dateutil.parser understands ISO8601 which is really handy.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
timestring: string with datetime
|
|
380
|
+
tz: optional timezone, if timezone is omitted from timestring.
|
|
381
|
+
Returns:
|
|
382
|
+
New Timestamp.
|
|
383
|
+
"""
|
|
384
|
+
r = parser.parse(timestring)
|
|
385
|
+
if not r.tzinfo:
|
|
386
|
+
r = (tz or cls.LocalTimezone).localize(r)
|
|
387
|
+
result = cls(r.year, r.month, r.day, r.hour, r.minute, r.second,
|
|
388
|
+
r.microsecond, r.tzinfo)
|
|
389
|
+
|
|
390
|
+
return result
|
|
391
|
+
|
|
392
|
+
@classmethod
|
|
393
|
+
def _IntStringToInterval(cls, timestring):
|
|
394
|
+
"""Parse interval date specification and create a timedelta object."""
|
|
395
|
+
return datetime.timedelta(seconds=ConvertIntervalToSeconds(timestring))
|
|
396
|
+
|
|
397
|
+
@classmethod
|
|
398
|
+
def FromString(cls, value, tz=None):
|
|
399
|
+
"""Try to create a Timestamp from a string."""
|
|
400
|
+
val = cls._StringToTime(value, tz)
|
|
401
|
+
if val:
|
|
402
|
+
return val
|
|
403
|
+
|
|
404
|
+
val = cls._IntStringToInterval(value)
|
|
405
|
+
if val:
|
|
406
|
+
return cls.utcnow() - val
|
|
407
|
+
|
|
408
|
+
raise TimeParseError(value)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
# What's written below is a clear python bug. I mean, okay, I can apply
|
|
412
|
+
# negative timezone to it and end result will be inconversible.
|
|
413
|
+
|
|
414
|
+
MAXIMUM_PYTHON_TIMESTAMP = Timestamp(
|
|
415
|
+
9999, 12, 31, 23, 59, 59, 999999, UTC)
|
|
416
|
+
|
|
417
|
+
# This is also a bug. It is called 32bit time_t. I hate it.
|
|
418
|
+
# This is fixed in 2.5, btw.
|
|
419
|
+
|
|
420
|
+
MAXIMUM_MICROSECOND_TIMESTAMP = 0x80000000 * _MICROSECONDS_PER_SECOND - 1
|
|
421
|
+
MAXIMUM_MICROSECOND_TIMESTAMP_AS_TS = Timestamp(2038, 1, 19, 3, 14, 7, 999999)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# Copyright 2004 Google Inc. All Rights Reserved.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS-IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
"""Import this module to add a hook to call pdb on uncaught exceptions.
|
|
17
|
+
|
|
18
|
+
To enable this, do the following in your top-level application:
|
|
19
|
+
|
|
20
|
+
import google.apputils.debug
|
|
21
|
+
|
|
22
|
+
and then in your main():
|
|
23
|
+
|
|
24
|
+
google.apputils.debug.Init()
|
|
25
|
+
|
|
26
|
+
Then run your program with --pdb.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
import sys
|
|
32
|
+
|
|
33
|
+
import gflags as flags
|
|
34
|
+
|
|
35
|
+
flags.DEFINE_boolean('pdb', 0, 'Drop into pdb on uncaught exceptions')
|
|
36
|
+
|
|
37
|
+
old_excepthook = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _DebugHandler(exc_class, value, tb):
|
|
41
|
+
if not flags.FLAGS.pdb or hasattr(sys, 'ps1') or not sys.stderr.isatty():
|
|
42
|
+
# we aren't in interactive mode or we don't have a tty-like
|
|
43
|
+
# device, so we call the default hook
|
|
44
|
+
old_excepthook(exc_class, value, tb)
|
|
45
|
+
else:
|
|
46
|
+
# Don't impose import overhead on apps that never raise an exception.
|
|
47
|
+
import traceback
|
|
48
|
+
import pdb
|
|
49
|
+
# we are in interactive mode, print the exception...
|
|
50
|
+
traceback.print_exception(exc_class, value, tb)
|
|
51
|
+
print
|
|
52
|
+
# ...then start the debugger in post-mortem mode.
|
|
53
|
+
pdb.pm()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def Init():
|
|
57
|
+
# Must back up old excepthook.
|
|
58
|
+
global old_excepthook # pylint: disable-msg=W0603
|
|
59
|
+
old_excepthook = sys.excepthook
|
|
60
|
+
sys.excepthook = _DebugHandler
|