diff --git a/.travis.yml b/.travis.yml
index b17ca01..b1a8204 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,12 @@
language: python
-python:
- - 2.7
+matrix:
+ include:
+ - python: 2.7
+ - python: 3.7
+ dist: xenial # required for Python >= 3.7 on Travis CI
+ allow_failures:
+ - python: 3.7
+before_install: pip install --upgrade pip
script: ./script/cibuild
notifications:
email:
diff --git a/octodns/cmds/report.py b/octodns/cmds/report.py
index 2b32e77..3a26052 100755
--- a/octodns/cmds/report.py
+++ b/octodns/cmds/report.py
@@ -13,6 +13,8 @@ from logging import getLogger
from sys import stdout
import re
+from six import text_type
+
from octodns.cmds.args import ArgumentParser
from octodns.manager import Manager
from octodns.zone import Zone
@@ -65,7 +67,7 @@ def main():
resolver = AsyncResolver(configure=False,
num_workers=int(args.num_workers))
if not ip_addr_re.match(server):
- server = unicode(query(server, 'A')[0])
+ server = text_type(query(server, 'A')[0])
log.info('server=%s', server)
resolver.nameservers = [server]
resolver.lifetime = int(args.timeout)
@@ -81,12 +83,12 @@ def main():
stdout.write(',')
stdout.write(record._type)
stdout.write(',')
- stdout.write(unicode(record.ttl))
+ stdout.write(text_type(record.ttl))
compare = {}
for future in futures:
stdout.write(',')
try:
- answers = [unicode(r) for r in future.result()]
+ answers = [text_type(r) for r in future.result()]
except (NoAnswer, NoNameservers):
answers = ['*no answer*']
except NXDOMAIN:
diff --git a/octodns/manager.py b/octodns/manager.py
index 4952315..89db845 100644
--- a/octodns/manager.py
+++ b/octodns/manager.py
@@ -276,9 +276,9 @@ class Manager(object):
try:
sources = [self.providers[source] for source in sources]
- except KeyError:
+ except KeyError as e:
raise Exception('Zone {}, unknown source: {}'.format(zone_name,
- source))
+ e))
try:
trgs = []
@@ -397,9 +397,9 @@ class Manager(object):
try:
sources = [self.providers[source] for source in sources]
- except KeyError:
+ except KeyError as e:
raise Exception('Zone {}, unknown source: {}'.format(zone_name,
- source))
+ e))
for source in sources:
if isinstance(source, YamlProvider):
diff --git a/octodns/provider/base.py b/octodns/provider/base.py
index 2c93e49..96d1be1 100644
--- a/octodns/provider/base.py
+++ b/octodns/provider/base.py
@@ -5,6 +5,8 @@
from __future__ import absolute_import, division, print_function, \
unicode_literals
+from six import text_type
+
from ..source.base import BaseSource
from ..zone import Zone
from .plan import Plan
@@ -68,7 +70,7 @@ class BaseProvider(BaseSource):
changes=changes)
if extra:
self.log.info('plan: extra changes\n %s', '\n '
- .join([unicode(c) for c in extra]))
+ .join([text_type(c) for c in extra]))
changes += extra
if changes:
diff --git a/octodns/provider/cloudflare.py b/octodns/provider/cloudflare.py
index 881c2fd..ac1fe9f 100644
--- a/octodns/provider/cloudflare.py
+++ b/octodns/provider/cloudflare.py
@@ -442,7 +442,7 @@ class CloudflareProvider(BaseProvider):
# Round trip the single value through a record to contents flow
# to get a consistent _gen_data result that matches what
# went in to new_contents
- data = self._gen_data(r).next()
+ data = next(self._gen_data(r))
# Record the record_id and data for this existing record
key = self._gen_key(data)
diff --git a/octodns/provider/ns1.py b/octodns/provider/ns1.py
index 5fdf5b0..d3faf21 100644
--- a/octodns/provider/ns1.py
+++ b/octodns/provider/ns1.py
@@ -13,6 +13,8 @@ from nsone.rest.errors import RateLimitException, ResourceException
from incf.countryutils import transformations
from time import sleep
+from six import text_type
+
from ..record import Record
from .base import BaseProvider
@@ -76,9 +78,9 @@ class Ns1Provider(BaseProvider):
else:
values.extend(answer['answer'])
codes.append([])
- values = [unicode(x) for x in values]
+ values = [text_type(x) for x in values]
geo = OrderedDict(
- {unicode(k): [unicode(x) for x in v] for k, v in geo.items()}
+ {text_type(k): [text_type(x) for x in v] for k, v in geo.items()}
)
data['values'] = values
data['geo'] = geo
diff --git a/octodns/provider/plan.py b/octodns/provider/plan.py
index bae244f..ad14b6d 100644
--- a/octodns/provider/plan.py
+++ b/octodns/provider/plan.py
@@ -9,6 +9,8 @@ from StringIO import StringIO
from logging import DEBUG, ERROR, INFO, WARN, getLogger
from sys import stdout
+from six import text_type
+
class UnsafePlan(Exception):
pass
@@ -147,11 +149,11 @@ class PlanLogger(_PlanOutput):
def _value_stringifier(record, sep):
try:
- values = [unicode(v) for v in record.values]
+ values = [text_type(v) for v in record.values]
except AttributeError:
values = [record.value]
for code, gv in sorted(getattr(record, 'geo', {}).items()):
- vs = ', '.join([unicode(v) for v in gv.values])
+ vs = ', '.join([text_type(v) for v in gv.values])
values.append('{}: {}'.format(code, vs))
return sep.join(values)
@@ -193,7 +195,7 @@ class PlanMarkdown(_PlanOutput):
fh.write(' | ')
# TTL
if existing:
- fh.write(unicode(existing.ttl))
+ fh.write(text_type(existing.ttl))
fh.write(' | ')
fh.write(_value_stringifier(existing, '; '))
fh.write(' | |\n')
@@ -201,7 +203,7 @@ class PlanMarkdown(_PlanOutput):
fh.write('| | | | ')
if new:
- fh.write(unicode(new.ttl))
+ fh.write(text_type(new.ttl))
fh.write(' | ')
fh.write(_value_stringifier(new, '; '))
fh.write(' | ')
@@ -210,7 +212,7 @@ class PlanMarkdown(_PlanOutput):
fh.write(' |\n')
fh.write('\nSummary: ')
- fh.write(unicode(plan))
+ fh.write(text_type(plan))
fh.write('\n\n')
else:
fh.write('## No changes were planned\n')
@@ -261,7 +263,7 @@ class PlanHtml(_PlanOutput):
# TTL
if existing:
fh.write('
')
- fh.write(unicode(existing.ttl))
+ fh.write(text_type(existing.ttl))
fh.write(' | \n ')
fh.write(_value_stringifier(existing, ' '))
fh.write(' | \n | \n \n')
@@ -270,7 +272,7 @@ class PlanHtml(_PlanOutput):
if new:
fh.write(' ')
- fh.write(unicode(new.ttl))
+ fh.write(text_type(new.ttl))
fh.write(' | \n ')
fh.write(_value_stringifier(new, ' '))
fh.write(' | \n ')
@@ -279,7 +281,7 @@ class PlanHtml(_PlanOutput):
fh.write(' | \n \n')
fh.write(' \n | Summary: ')
- fh.write(unicode(plan))
+ fh.write(text_type(plan))
fh.write(' | \n
\n\n')
else:
fh.write('No changes were planned')
diff --git a/octodns/provider/route53.py b/octodns/provider/route53.py
index 4cf7c99..7154096 100644
--- a/octodns/provider/route53.py
+++ b/octodns/provider/route53.py
@@ -14,10 +14,17 @@ from uuid import uuid4
import logging
import re
+from six import text_type
+
from ..record import Record, Update
from ..record.geo import GeoCodes
from .base import BaseProvider
+try:
+ cmp
+except NameError:
+ def cmp(x, y):
+ return (x > y) - (x < y)
octal_re = re.compile(r'\\(\d\d\d)')
@@ -1037,8 +1044,8 @@ class Route53Provider(BaseProvider):
# ip_address's returned object for equivalence
# E.g 2001:4860:4860::8842 -> 2001:4860:4860:0:0:0:0:8842
if value:
- value = ip_address(unicode(value))
- config_ip_address = ip_address(unicode(config['IPAddress']))
+ value = ip_address(text_type(value))
+ config_ip_address = ip_address(text_type(config['IPAddress']))
else:
# No value so give this a None to match value's
config_ip_address = None
@@ -1059,7 +1066,7 @@ class Route53Provider(BaseProvider):
fqdn, record._type, value)
try:
- ip_address(unicode(value))
+ ip_address(text_type(value))
# We're working with an IP, host is the Host header
healthcheck_host = record.healthcheck_host
except (AddressValueError, ValueError):
diff --git a/octodns/record/__init__.py b/octodns/record/__init__.py
index dca6100..2efdf0e 100644
--- a/octodns/record/__init__.py
+++ b/octodns/record/__init__.py
@@ -9,8 +9,16 @@ from ipaddress import IPv4Address, IPv6Address
from logging import getLogger
import re
+from six import string_types, text_type
+
from .geo import GeoCodes
+try:
+ cmp
+except NameError:
+ def cmp(x, y):
+ return (x > y) - (x < y)
+
class Change(object):
@@ -130,7 +138,7 @@ class Record(object):
self.__class__.__name__, name)
self.zone = zone
# force everything lower-case just to be safe
- self.name = unicode(name).lower() if name else name
+ self.name = text_type(name).lower() if name else name
self.source = source
self.ttl = int(data['ttl'])
@@ -292,7 +300,7 @@ class _ValuesMixin(object):
return ret
def __repr__(self):
- values = "['{}']".format("', '".join([unicode(v)
+ values = "['{}']".format("', '".join([text_type(v)
for v in self.values]))
return '<{} {} {}, {}, {}>'.format(self.__class__.__name__,
self._type, self.ttl,
@@ -574,7 +582,7 @@ class _DynamicMixin(object):
reasons.append('rule {} missing pool'.format(rule_num))
continue
- if not isinstance(pool, basestring):
+ if not isinstance(pool, string_types):
reasons.append('rule {} invalid pool "{}"'
.format(rule_num, pool))
elif pool not in pools:
@@ -671,13 +679,13 @@ class _IpList(object):
return ['missing value(s)']
reasons = []
for value in data:
- if value is '':
+ if value == '':
reasons.append('empty value')
elif value is None:
reasons.append('missing value(s)')
else:
try:
- cls._address_type(unicode(value))
+ cls._address_type(text_type(value))
except Exception:
reasons.append('invalid {} address "{}"'
.format(cls._address_name, value))
diff --git a/octodns/zone.py b/octodns/zone.py
index 916f81b..1191b6f 100644
--- a/octodns/zone.py
+++ b/octodns/zone.py
@@ -9,6 +9,8 @@ from collections import defaultdict
from logging import getLogger
import re
+from six import text_type
+
from .record import Create, Delete
@@ -38,7 +40,7 @@ class Zone(object):
raise Exception('Invalid zone name {}, missing ending dot'
.format(name))
# Force everything to lowercase just to be safe
- self.name = unicode(name).lower() if name else name
+ self.name = text_type(name).lower() if name else name
self.sub_zones = sub_zones
# We're grouping by node, it allows us to efficiently search for
# duplicates and detect when CNAMEs co-exist with other records
diff --git a/requirements.txt b/requirements.txt
index d100c96..63adffa 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,7 +6,7 @@ botocore==1.10.5
dnspython==1.15.0
docutils==0.14
dyn==1.8.1
-futures==3.2.0
+futures==3.2.0; python_version < '3.0'
google-cloud-core==0.28.1
google-cloud-dns==0.29.0
incf.countryutils==1.0
@@ -19,5 +19,5 @@ ovh==0.4.8
python-dateutil==2.6.1
requests==2.20.0
s3transfer==0.1.13
-six==1.11.0
+six==1.12.0
setuptools==38.5.2
diff --git a/tests/test_octodns_provider_base.py b/tests/test_octodns_provider_base.py
index e28850a..b0e2f8e 100644
--- a/tests/test_octodns_provider_base.py
+++ b/tests/test_octodns_provider_base.py
@@ -8,6 +8,8 @@ from __future__ import absolute_import, division, print_function, \
from logging import getLogger
from unittest import TestCase
+from six import text_type
+
from octodns.record import Create, Delete, Record, Update
from octodns.provider.base import BaseProvider
from octodns.provider.plan import Plan, UnsafePlan
@@ -193,7 +195,7 @@ class TestBaseProvider(TestCase):
})
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
- zone.add_record(Record.new(zone, unicode(i), {
+ zone.add_record(Record.new(zone, text_type(i), {
'ttl': 60,
'type': 'A',
'value': '2.3.4.5'
@@ -225,7 +227,7 @@ class TestBaseProvider(TestCase):
})
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
- zone.add_record(Record.new(zone, unicode(i), {
+ zone.add_record(Record.new(zone, text_type(i), {
'ttl': 60,
'type': 'A',
'value': '2.3.4.5'
@@ -251,7 +253,7 @@ class TestBaseProvider(TestCase):
})
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
- zone.add_record(Record.new(zone, unicode(i), {
+ zone.add_record(Record.new(zone, text_type(i), {
'ttl': 60,
'type': 'A',
'value': '2.3.4.5'
@@ -273,7 +275,7 @@ class TestBaseProvider(TestCase):
})
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
- zone.add_record(Record.new(zone, unicode(i), {
+ zone.add_record(Record.new(zone, text_type(i), {
'ttl': 60,
'type': 'A',
'value': '2.3.4.5'
@@ -299,7 +301,7 @@ class TestBaseProvider(TestCase):
})
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
- zone.add_record(Record.new(zone, unicode(i), {
+ zone.add_record(Record.new(zone, text_type(i), {
'ttl': 60,
'type': 'A',
'value': '2.3.4.5'
@@ -322,7 +324,7 @@ class TestBaseProvider(TestCase):
})
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
- zone.add_record(Record.new(zone, unicode(i), {
+ zone.add_record(Record.new(zone, text_type(i), {
'ttl': 60,
'type': 'A',
'value': '2.3.4.5'
@@ -350,7 +352,7 @@ class TestBaseProvider(TestCase):
})
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
- zone.add_record(Record.new(zone, unicode(i), {
+ zone.add_record(Record.new(zone, text_type(i), {
'ttl': 60,
'type': 'A',
'value': '2.3.4.5'
diff --git a/tests/test_octodns_provider_cloudflare.py b/tests/test_octodns_provider_cloudflare.py
index 25f2b58..9e25ea9 100644
--- a/tests/test_octodns_provider_cloudflare.py
+++ b/tests/test_octodns_provider_cloudflare.py
@@ -950,7 +950,7 @@ class TestCloudflareProvider(TestCase):
'value': 'ns1.unit.tests.'
})
- data = provider._gen_data(record).next()
+ data = next(provider._gen_data(record))
self.assertFalse('proxied' in data)
@@ -965,7 +965,7 @@ class TestCloudflareProvider(TestCase):
}), False
)
- data = provider._gen_data(record).next()
+ data = next(provider._gen_data(record))
self.assertFalse(data['proxied'])
@@ -980,7 +980,7 @@ class TestCloudflareProvider(TestCase):
}), True
)
- data = provider._gen_data(record).next()
+ data = next(provider._gen_data(record))
self.assertTrue(data['proxied'])
diff --git a/tests/test_octodns_provider_googlecloud.py b/tests/test_octodns_provider_googlecloud.py
index 3a3e600..d7f0e0c 100644
--- a/tests/test_octodns_provider_googlecloud.py
+++ b/tests/test_octodns_provider_googlecloud.py
@@ -194,7 +194,7 @@ class DummyIterator:
return self
def next(self):
- return self.iterable.next()
+ return next(self.iterable)
class TestGoogleCloudProvider(TestCase):