Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 99e04fd

Browse filesBrowse files
committed
Merge pull request #1044 from dhermes/remove-pytz-dependency
Removing use of pytz module / implementing our own UTC.
2 parents 3b27808 + b8bce16 commit 99e04fd
Copy full SHA for 99e04fd
Expand file treeCollapse file tree

18 files changed

+193
-78
lines changed

‎gcloud/_helpers.py

Copy file name to clipboardExpand all lines: gcloud/_helpers.py
+43Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
1616
This module is not part of the public API surface of `gcloud`.
1717
"""
18+
19+
import datetime
1820
import os
1921
import socket
2022

@@ -75,6 +77,41 @@ def top(self):
7577
return self._stack[-1]
7678

7779

80+
class _UTC(datetime.tzinfo):
81+
"""Basic UTC implementation.
82+
83+
Implementing a small surface area to avoid depending on ``pytz``.
84+
"""
85+
86+
_dst = datetime.timedelta(0)
87+
_tzname = 'UTC'
88+
_utcoffset = _dst
89+
90+
def dst(self, dt): # pylint: disable=unused-argument
91+
"""Daylight savings time offset."""
92+
return self._dst
93+
94+
def fromutc(self, dt):
95+
"""Convert a timestamp from (naive) UTC to this timezone."""
96+
if dt.tzinfo is None:
97+
return dt.replace(tzinfo=self)
98+
return super(_UTC, self).fromutc(dt)
99+
100+
def tzname(self, dt): # pylint: disable=unused-argument
101+
"""Get the name of this timezone."""
102+
return self._tzname
103+
104+
def utcoffset(self, dt): # pylint: disable=unused-argument
105+
"""UTC offset of this timezone."""
106+
return self._utcoffset
107+
108+
def __repr__(self):
109+
return '<%s>' % (self._tzname,)
110+
111+
def __str__(self):
112+
return self._tzname
113+
114+
78115
def _ensure_tuple_or_list(arg_name, tuple_or_list):
79116
"""Ensures an input is a tuple or list.
80117
@@ -168,3 +205,9 @@ def _determine_default_project(project=None):
168205
project = _get_production_project()
169206

170207
return project
208+
209+
210+
try:
211+
from pytz import UTC # pylint: disable=unused-import
212+
except ImportError:
213+
UTC = _UTC() # Singleton instance to be used throughout.

‎gcloud/bigquery/_helpers.py

Copy file name to clipboardExpand all lines: gcloud/bigquery/_helpers.py
+4-3Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818
import datetime
1919
import sys
2020

21-
import pytz
21+
from gcloud._helpers import UTC
2222

23-
_EPOCH = datetime.datetime.utcfromtimestamp(0).replace(tzinfo=pytz.utc)
23+
24+
_EPOCH = datetime.datetime.utcfromtimestamp(0).replace(tzinfo=UTC)
2425

2526

2627
def _millis(when):
@@ -62,7 +63,7 @@ def _prop_from_datetime(value):
6263
if value is not None:
6364
if value.tzinfo is None:
6465
# Assume UTC
65-
value = value.replace(tzinfo=pytz.utc)
66+
value = value.replace(tzinfo=UTC)
6667
# back-end wants timestamps as milliseconds since the epoch
6768
return _millis(value)
6869

‎gcloud/bigquery/test__helpers.py

Copy file name to clipboardExpand all lines: gcloud/bigquery/test__helpers.py
+25-15Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ def _callFUT(self, value):
2323

2424
def test_one_second_from_epoch(self):
2525
import datetime
26-
import pytz
27-
WHEN = datetime.datetime(1970, 1, 1, 0, 0, 1, tzinfo=pytz.utc)
26+
from gcloud._helpers import UTC
27+
28+
WHEN = datetime.datetime(1970, 1, 1, 0, 0, 1, tzinfo=UTC)
2829
self.assertEqual(self._callFUT(WHEN), 1000)
2930

3031

@@ -39,11 +40,12 @@ def test_w_none(self):
3940

4041
def test_w_millis(self):
4142
import datetime
42-
import pytz
43+
from gcloud._helpers import UTC
4344
from gcloud.bigquery._helpers import _total_seconds
45+
4446
NOW = datetime.datetime(2015, 7, 29, 17, 45, 21, 123456,
45-
tzinfo=pytz.utc)
46-
EPOCH = datetime.datetime(1970, 1, 1, tzinfo=pytz.utc)
47+
tzinfo=UTC)
48+
EPOCH = datetime.datetime(1970, 1, 1, tzinfo=UTC)
4749
MILLIS = _total_seconds(NOW - EPOCH) * 1000
4850
self.assertEqual(self._callFUT(MILLIS), NOW)
4951

@@ -59,34 +61,42 @@ def test_w_none(self):
5961

6062
def test_w_utc_datetime(self):
6163
import datetime
62-
import pytz
64+
from gcloud._helpers import UTC
6365
from gcloud.bigquery._helpers import _total_seconds
64-
NOW = datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
65-
EPOCH = datetime.datetime(1970, 1, 1, tzinfo=pytz.utc)
66+
67+
NOW = datetime.datetime.utcnow().replace(tzinfo=UTC)
68+
EPOCH = datetime.datetime(1970, 1, 1, tzinfo=UTC)
6669
MILLIS = int(_total_seconds(NOW - EPOCH) * 1000)
6770
result = self._callFUT(NOW)
6871
self.assertTrue(isinstance(result, int))
6972
self.assertEqual(result, MILLIS)
7073

7174
def test_w_non_utc_datetime(self):
7275
import datetime
73-
import pytz
76+
from gcloud._helpers import UTC
77+
from gcloud._helpers import _UTC
7478
from gcloud.bigquery._helpers import _total_seconds
75-
eastern = pytz.timezone('US/Eastern')
76-
NOW = datetime.datetime(2015, 7, 28, 16, 34, 47, tzinfo=eastern)
77-
EPOCH = datetime.datetime(1970, 1, 1, tzinfo=pytz.utc)
79+
80+
class CET(_UTC):
81+
_tzname = 'CET'
82+
_utcoffset = datetime.timedelta(hours=-1)
83+
84+
zone = CET()
85+
NOW = datetime.datetime(2015, 7, 28, 16, 34, 47, tzinfo=zone)
86+
EPOCH = datetime.datetime(1970, 1, 1, tzinfo=UTC)
7887
MILLIS = int(_total_seconds(NOW - EPOCH) * 1000)
7988
result = self._callFUT(NOW)
8089
self.assertTrue(isinstance(result, int))
8190
self.assertEqual(result, MILLIS)
8291

8392
def test_w_naive_datetime(self):
8493
import datetime
85-
import pytz
94+
from gcloud._helpers import UTC
8695
from gcloud.bigquery._helpers import _total_seconds
96+
8797
NOW = datetime.datetime.utcnow()
88-
UTC_NOW = NOW.replace(tzinfo=pytz.utc)
89-
EPOCH = datetime.datetime(1970, 1, 1, tzinfo=pytz.utc)
98+
UTC_NOW = NOW.replace(tzinfo=UTC)
99+
EPOCH = datetime.datetime(1970, 1, 1, tzinfo=UTC)
90100
MILLIS = int(_total_seconds(UTC_NOW - EPOCH) * 1000)
91101
result = self._callFUT(NOW)
92102
self.assertTrue(isinstance(result, int))

‎gcloud/bigquery/test_dataset.py

Copy file name to clipboardExpand all lines: gcloud/bigquery/test_dataset.py
+3-2Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ def _makeOne(self, *args, **kw):
2828

2929
def _makeResource(self):
3030
import datetime
31-
import pytz
31+
from gcloud._helpers import UTC
32+
3233
self.WHEN_TS = 1437767599.006
3334
self.WHEN = datetime.datetime.utcfromtimestamp(self.WHEN_TS).replace(
34-
tzinfo=pytz.UTC)
35+
tzinfo=UTC)
3536
self.ETAG = 'ETAG'
3637
self.DS_ID = '%s:%s' % (self.PROJECT, self.DS_NAME)
3738
self.RESOURCE_URL = 'http://example.com/path/to/resource'

‎gcloud/bigquery/test_table.py

Copy file name to clipboardExpand all lines: gcloud/bigquery/test_table.py
+25-17Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,11 @@ def _makeOne(self, *args, **kw):
7676

7777
def _makeResource(self):
7878
import datetime
79-
import pytz
79+
from gcloud._helpers import UTC
80+
8081
self.WHEN_TS = 1437767599.006
8182
self.WHEN = datetime.datetime.utcfromtimestamp(self.WHEN_TS).replace(
82-
tzinfo=pytz.UTC)
83+
tzinfo=UTC)
8384
self.ETAG = 'ETAG'
8485
self.TABLE_ID = '%s:%s:%s' % (
8586
self.PROJECT, self.DS_NAME, self.TABLE_NAME)
@@ -206,10 +207,11 @@ def test_schema_setter(self):
206207

207208
def test_props_set_by_server(self):
208209
import datetime
209-
import pytz
210+
from gcloud._helpers import UTC
210211
from gcloud.bigquery._helpers import _millis
211-
CREATED = datetime.datetime(2015, 7, 29, 12, 13, 22, tzinfo=pytz.utc)
212-
MODIFIED = datetime.datetime(2015, 7, 29, 14, 47, 15, tzinfo=pytz.utc)
212+
213+
CREATED = datetime.datetime(2015, 7, 29, 12, 13, 22, tzinfo=UTC)
214+
MODIFIED = datetime.datetime(2015, 7, 29, 14, 47, 15, tzinfo=UTC)
213215
TABLE_ID = '%s:%s:%s' % (
214216
self.PROJECT, self.DS_NAME, self.TABLE_NAME)
215217
URL = 'http://example.com/projects/%s/datasets/%s/tables/%s' % (
@@ -258,8 +260,9 @@ def test_expires_setter_bad_value(self):
258260

259261
def test_expires_setter(self):
260262
import datetime
261-
import pytz
262-
WHEN = datetime.datetime(2015, 7, 28, 16, 39, tzinfo=pytz.utc)
263+
from gcloud._helpers import UTC
264+
265+
WHEN = datetime.datetime(2015, 7, 28, 16, 39, tzinfo=UTC)
263266
client = _Client(self.PROJECT)
264267
dataset = _Dataset(client)
265268
table = self._makeOne(self.TABLE_NAME, dataset)
@@ -443,9 +446,10 @@ def test_create_w_bound_client(self):
443446

444447
def test_create_w_alternate_client(self):
445448
import datetime
446-
import pytz
449+
from gcloud._helpers import UTC
447450
from gcloud.bigquery.table import SchemaField
448451
from gcloud.bigquery._helpers import _millis
452+
449453
PATH = 'projects/%s/datasets/%s/tables' % (self.PROJECT, self.DS_NAME)
450454
DESCRIPTION = 'DESCRIPTION'
451455
TITLE = 'TITLE'
@@ -454,7 +458,7 @@ def test_create_w_alternate_client(self):
454458
RESOURCE['description'] = DESCRIPTION
455459
RESOURCE['friendlyName'] = TITLE
456460
self.EXP_TIME = datetime.datetime(2015, 8, 1, 23, 59, 59,
457-
tzinfo=pytz.utc)
461+
tzinfo=UTC)
458462
RESOURCE['expirationTime'] = _millis(self.EXP_TIME)
459463
RESOURCE['view'] = {}
460464
RESOURCE['view']['query'] = QUERY
@@ -642,9 +646,10 @@ def test_patch_w_bound_client(self):
642646

643647
def test_patch_w_alternate_client(self):
644648
import datetime
645-
import pytz
649+
from gcloud._helpers import UTC
646650
from gcloud.bigquery._helpers import _millis
647651
from gcloud.bigquery.table import SchemaField
652+
648653
PATH = 'projects/%s/datasets/%s/tables/%s' % (
649654
self.PROJECT, self.DS_NAME, self.TABLE_NAME)
650655
QUERY = 'select fullname, age from person_ages'
@@ -654,7 +659,7 @@ def test_patch_w_alternate_client(self):
654659
RESOURCE['type'] = 'VIEW'
655660
RESOURCE['location'] = LOCATION
656661
self.EXP_TIME = datetime.datetime(2015, 8, 1, 23, 59, 59,
657-
tzinfo=pytz.utc)
662+
tzinfo=UTC)
658663
RESOURCE['expirationTime'] = _millis(self.EXP_TIME)
659664
conn1 = _Connection()
660665
client1 = _Client(project=self.PROJECT, connection=conn1)
@@ -750,9 +755,10 @@ def test_update_w_bound_client(self):
750755

751756
def test_update_w_alternate_client(self):
752757
import datetime
753-
import pytz
758+
from gcloud._helpers import UTC
754759
from gcloud.bigquery._helpers import _millis
755760
from gcloud.bigquery.table import SchemaField
761+
756762
PATH = 'projects/%s/datasets/%s/tables/%s' % (
757763
self.PROJECT, self.DS_NAME, self.TABLE_NAME)
758764
DEF_TABLE_EXP = 12345
@@ -762,7 +768,7 @@ def test_update_w_alternate_client(self):
762768
RESOURCE['defaultTableExpirationMs'] = 12345
763769
RESOURCE['location'] = LOCATION
764770
self.EXP_TIME = datetime.datetime(2015, 8, 1, 23, 59, 59,
765-
tzinfo=pytz.utc)
771+
tzinfo=UTC)
766772
RESOURCE['expirationTime'] = _millis(self.EXP_TIME)
767773
RESOURCE['view'] = {'query': QUERY}
768774
RESOURCE['type'] = 'VIEW'
@@ -837,14 +843,15 @@ def test_delete_w_alternate_client(self):
837843

838844
def test_fetch_data_w_bound_client(self):
839845
import datetime
840-
import pytz
846+
from gcloud._helpers import UTC
841847
from gcloud.bigquery.table import SchemaField
842848
from gcloud.bigquery._helpers import _prop_from_datetime
849+
843850
PATH = 'projects/%s/datasets/%s/tables/%s/data' % (
844851
self.PROJECT, self.DS_NAME, self.TABLE_NAME)
845852
WHEN_TS = 1437767599.006
846853
WHEN = datetime.datetime.utcfromtimestamp(WHEN_TS).replace(
847-
tzinfo=pytz.UTC)
854+
tzinfo=UTC)
848855
WHEN_1 = WHEN + datetime.timedelta(seconds=1)
849856
WHEN_2 = WHEN + datetime.timedelta(seconds=2)
850857
ROWS = 1234
@@ -1068,12 +1075,13 @@ def test_fetch_data_w_record_schema(self):
10681075

10691076
def test_insert_data_w_bound_client(self):
10701077
import datetime
1071-
import pytz
1078+
from gcloud._helpers import UTC
10721079
from gcloud.bigquery._helpers import _prop_from_datetime
10731080
from gcloud.bigquery.table import SchemaField
1081+
10741082
WHEN_TS = 1437767599.006
10751083
WHEN = datetime.datetime.utcfromtimestamp(WHEN_TS).replace(
1076-
tzinfo=pytz.UTC)
1084+
tzinfo=UTC)
10771085
PATH = 'projects/%s/datasets/%s/tables/%s/insertAll' % (
10781086
self.PROJECT, self.DS_NAME, self.TABLE_NAME)
10791087
conn = _Connection({})

‎gcloud/credentials.py

Copy file name to clipboardExpand all lines: gcloud/credentials.py
+5-4Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
from oauth2client.client import _get_application_default_credential_from_file
2828
from oauth2client import crypt
2929
from oauth2client import service_account
30-
import pytz
3130

3231
try:
3332
from google.appengine.api import app_identity
@@ -40,6 +39,8 @@
4039
class _GAECreds(object):
4140
"""Dummy class if not in App Engine environment."""
4241

42+
from gcloud._helpers import UTC
43+
4344

4445
def get_credentials():
4546
"""Gets credentials implicitly from the current environment.
@@ -274,17 +275,17 @@ def _get_expiration_seconds(expiration):
274275
"""
275276
# If it's a timedelta, add it to `now` in UTC.
276277
if isinstance(expiration, datetime.timedelta):
277-
now = _utcnow().replace(tzinfo=pytz.utc)
278+
now = _utcnow().replace(tzinfo=UTC)
278279
expiration = now + expiration
279280

280281
# If it's a datetime, convert to a timestamp.
281282
if isinstance(expiration, datetime.datetime):
282283
# Make sure the timezone on the value is UTC
283284
# (either by converting or replacing the value).
284285
if expiration.tzinfo:
285-
expiration = expiration.astimezone(pytz.utc)
286+
expiration = expiration.astimezone(UTC)
286287
else:
287-
expiration = expiration.replace(tzinfo=pytz.utc)
288+
expiration = expiration.replace(tzinfo=UTC)
288289

289290
# Turn the datetime into a timestamp (seconds, not microseconds).
290291
expiration = int(calendar.timegm(expiration.timetuple()))

‎gcloud/datastore/helpers.py

Copy file name to clipboardExpand all lines: gcloud/datastore/helpers.py
+4-4Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
import datetime
2222

2323
from google.protobuf.internal.type_checkers import Int64ValueChecker
24-
import pytz
2524
import six
2625

26+
from gcloud._helpers import UTC
2727
from gcloud.datastore import _datastore_v1_pb2 as datastore_pb
2828
from gcloud.datastore.entity import Entity
2929
from gcloud.datastore.key import Key
@@ -185,9 +185,9 @@ def _pb_attr_value(val):
185185
# If the datetime is naive (no timezone), consider that it was
186186
# intended to be UTC and replace the tzinfo to that effect.
187187
if not val.tzinfo:
188-
val = val.replace(tzinfo=pytz.utc)
188+
val = val.replace(tzinfo=UTC)
189189
# Regardless of what timezone is on the value, convert it to UTC.
190-
val = val.astimezone(pytz.utc)
190+
val = val.astimezone(UTC)
191191
# Convert the datetime to a microsecond timestamp.
192192
value = int(calendar.timegm(val.timetuple()) * 1e6) + val.microsecond
193193
elif isinstance(val, Key):
@@ -233,7 +233,7 @@ def _get_value_from_value_pb(value_pb):
233233
microseconds = value_pb.timestamp_microseconds_value
234234
naive = (datetime.datetime.utcfromtimestamp(0) +
235235
datetime.timedelta(microseconds=microseconds))
236-
result = naive.replace(tzinfo=pytz.utc)
236+
result = naive.replace(tzinfo=UTC)
237237

238238
elif value_pb.HasField('key_value'):
239239
result = key_from_protobuf(value_pb.key_value)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.