Skip to content

Commit 3bd8ee9

Browse files
Merge pull request #149 from vrusinov/py3
Drop Python2 support and remove code supporting it
2 parents 2010828 + 07cf4c1 commit 3bd8ee9

18 files changed

+50
-109
lines changed

Diff for: .github/workflows/test.yml

+1-3
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@ jobs:
99
runs-on: ubuntu-latest
1010
strategy:
1111
matrix:
12-
python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
12+
python-version: [3.5, 3.6, 3.7, 3.8]
1313
taskwarrior-version: [2.5.0, 2.5.1, 2.5.3]
1414
exclude:
1515
# Taskwarriror 3.5.3 only supported on Python 3.7+.
16-
- python-version: 2.7
17-
taskwarrior-version: 2.5.3
1816
- python-version: 3.5
1917
taskwarrior-version: 2.5.3
2018
- python-version: 3.6

Diff for: requirements.txt

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
six
21
python-dateutil
32
pytz
43
kitchen

Diff for: setup.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,18 @@
3737
]
3838
REQUIREMENTS[category] = requirements
3939

40-
if sys.version_info < (2, 7):
41-
REQUIREMENTS['test'].append('unittest2')
42-
REQUIREMENTS['install'].append('ordereddict')
43-
4440
setup(name='taskw',
4541
version='1.3.1',
4642
description="Python bindings for your taskwarrior database",
4743
long_description=long_description,
4844
classifiers=[
4945
"Development Status :: 5 - Production/Stable",
50-
"Programming Language :: Python :: 2",
51-
"Programming Language :: Python :: 2.7",
5246
"Programming Language :: Python :: 3",
5347
"Programming Language :: Python :: 3.4",
5448
"Programming Language :: Python :: 3.5",
5549
"Programming Language :: Python :: 3.6",
50+
"Programming Language :: Python :: 3.7",
51+
"Programming Language :: Python :: 3.8",
5652
"License :: OSI Approved :: GNU General Public License (GPL)",
5753
"Intended Audience :: Developers",
5854
],

Diff for: taskw/exceptions.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,10 @@ def __init__(self, command, stderr, stdout, code):
99
self.code = code
1010
super(TaskwarriorError, self).__init__(self.stderr)
1111

12-
def __unicode__(self):
12+
def __str__(self):
1313
return "%r #%s; stderr:\"%s\"; stdout:\"%s\"" % (
1414
self.command,
1515
self.code,
1616
self.stderr,
1717
self.stdout,
1818
)
19-
20-
def __str__(self):
21-
return self.__unicode__().encode(sys.getdefaultencoding(), 'replace')

Diff for: taskw/fields/annotationarray.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
from dateutil.parser import parse
2-
import six
32

43
from .array import ArrayField
54
from .base import DirtyableList
65

76

8-
class Annotation(six.text_type):
7+
class Annotation(str):
98
""" A special type of string that we'll use for storing annotations.
109
1110
This is, for all intents and purposes, really just a string, but
@@ -14,7 +13,7 @@ class Annotation(six.text_type):
1413
1514
"""
1615
def __new__(self, description, entry=None):
17-
return six.text_type.__new__(self, description)
16+
return str.__new__(self, description)
1817

1918
def __init__(self, description, entry=None):
2019
self._entry = entry
@@ -59,5 +58,5 @@ def serialize(self, value):
5958
if not value:
6059
value = []
6160
return super(AnnotationArrayField, self).serialize(
62-
[six.text_type(entry) for entry in value]
61+
[str(entry) for entry in value]
6362
)

Diff for: taskw/fields/base.py

+2-9
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import copy
22
import sys
33

4-
import six
5-
64

75
class Field(object):
86
def __init__(self, label=None, read_only=False):
@@ -25,17 +23,12 @@ def serialize(self, value):
2523
return value
2624

2725
def __str__(self):
28-
if sys.version_info >= (3, ):
29-
return self.label
30-
return self.__unicode__().encode(sys.getdefaultencoding(), 'replace')
31-
32-
def __unicode__(self):
3326
return self.label
3427

3528
def __repr__(self):
3629
return "<{cls} '{label}'>".format(
37-
cls=six.text_type(self.__class__.__name__),
38-
label=six.text_type(self) if self._label else '(No Label)',
30+
cls=str(self.__class__.__name__),
31+
label=str(self) if self._label else '(No Label)',
3932
)
4033

4134
def __eq__(self, other):

Diff for: taskw/fields/commaseparateduuid.py

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
from __future__ import absolute_import
2-
31
from distutils.version import LooseVersion
42

53
import uuid

Diff for: taskw/fields/string.py

+6-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import logging
22

3-
import six
4-
53
from taskw.utils import encode_replacements_experimental
64
from .base import Field
75

@@ -14,24 +12,24 @@ def deserialize(self, value):
1412
# If value is None, let's just let it pass through
1513
if not value:
1614
return value
17-
if not isinstance(value, six.string_types):
18-
value = six.text_type(value)
19-
for left, right in six.iteritems(encode_replacements_experimental):
15+
if not isinstance(value, str):
16+
value = str(value)
17+
for left, right in encode_replacements_experimental.items():
2018
value = value.replace(right, left)
2119
return value
2220

2321
def serialize(self, value):
2422
# If value is None let it pass through
2523
if not value:
2624
return value
27-
if not isinstance(value, six.string_types):
28-
string_value = six.text_type(value)
25+
if not isinstance(value, str):
26+
string_value = str(value)
2927
logger.debug(
3028
"Value %s serialized to string as '%s'",
3129
repr(value),
3230
string_value
3331
)
3432
value = string_value
35-
for left, right in six.iteritems(encode_replacements_experimental):
33+
for left, right in encode_replacements_experimental.items():
3634
value = value.replace(left, right)
3735
return value

Diff for: taskw/fields/uuid.py

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
from __future__ import absolute_import
2-
31
import uuid
42

53
from .base import Field

Diff for: taskw/task.py

+6-8
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
import os
44
import sys
55

6-
import six
7-
86
from taskw.fields import (
97
AnnotationArrayField,
108
ArrayField,
@@ -73,7 +71,7 @@ def __init__(self, data, udas=None):
7371
self._changes = []
7472

7573
processed = {}
76-
for k, v in six.iteritems(data):
74+
for k, v in data.items():
7775
processed[k] = self._deserialize(k, v, self._fields)
7876

7977
super(Task, self).__init__(processed)
@@ -87,7 +85,7 @@ def from_stub(cls, data, udas=None):
8785
fields.update(udas)
8886

8987
processed = {}
90-
for k, v in six.iteritems(data):
88+
for k, v in data.items():
9189
processed[k] = cls._serialize(k, v, fields)
9290

9391
return cls(processed, udas)
@@ -173,7 +171,7 @@ def get_changes(self, serialized=False, keep=False):
173171
)
174172

175173
# Check for changes on subordinate items
176-
for k, v in six.iteritems(self):
174+
for k, v in self.items():
177175
if isinstance(v, Dirtyable):
178176
result = v.get_changes(keep=keep)
179177
if result:
@@ -198,7 +196,7 @@ def update(self, values, force=False):
198196
199197
"""
200198
results = {}
201-
for k, v in six.iteritems(values):
199+
for k, v in values.items():
202200
results[k] = self.__setitem__(k, v, force=force)
203201
return results
204202

@@ -209,13 +207,13 @@ def set(self, key, value):
209207
def serialized(self):
210208
""" Returns a serialized representation of this task."""
211209
serialized = {}
212-
for k, v in six.iteritems(self):
210+
for k, v in self.items():
213211
serialized[k] = self._serialize(k, v, self._fields)
214212
return serialized
215213

216214
def serialized_changes(self, keep=False):
217215
serialized = {}
218-
for k, v in six.iteritems(self.get_changes(keep=keep)):
216+
for k, v in self.get_changes(keep=keep).items():
219217
# Here, `v` is a 2-tuple of the field's original value
220218
# and the field's new value.
221219
_, to = v

Diff for: taskw/taskrc.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import codecs
21
import logging
32
import os
43

@@ -93,7 +92,7 @@ def _merge_trees(self, left, right):
9392

9493
def _read(self, path):
9594
config = {}
96-
with codecs.open(path, 'r', 'utf8') as config_file:
95+
with open(path, 'r') as config_file:
9796
for raw_line in config_file.readlines():
9897
line = sanitize(raw_line)
9998
if not line:
@@ -158,10 +157,7 @@ def get_udas(self):
158157

159158
return udas
160159

161-
def __unicode__(self):
160+
def __str__(self):
162161
return 'TaskRc file at {path}'.format(
163162
path=self.path
164163
)
165-
166-
def __str__(self):
167-
return self.__unicode__().encode('utf-8', 'REPLACE')

Diff for: taskw/test/test_fields.py

+2-8
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,11 @@
44

55
from dateutil.tz import tzlocal
66
from pytz import UTC, timezone
7-
import six
87

98
from taskw import fields
109
from taskw.fields.annotationarray import Annotation
1110

12-
if sys.version_info >= (2, 7):
13-
from unittest import TestCase
14-
else:
15-
from unittest2 import TestCase
11+
from unittest import TestCase
1612

1713

1814
class TestAnnotationArrayField(TestCase):
@@ -36,9 +32,7 @@ def test_serialize_annotations_into_strings(self):
3632

3733
self.assertEqual(actual_serialized, expected_serialized)
3834
for entry in actual_serialized:
39-
self.assertTrue(
40-
isinstance(entry, six.text_type)
41-
)
35+
self.assertTrue(isinstance(entry, str))
4236

4337
def test_deserialize_fully_formed_entries_to_stringey_things(self):
4438
# Note that this test is *identical* in conditions and actions

Diff for: taskw/test/test_task.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import copy
22
import datetime
3+
import io
34
import uuid
45
from unittest import TestCase
56

67
import pytz
7-
import six
88
from dateutil.tz import tzutc
99

1010
from taskw.task import Task
@@ -41,7 +41,7 @@ def test_marks_date_changed(self):
4141
expected_changes = {'due': (original_due_date, new_due_date)}
4242
actual_changes = self.task.get_changes()
4343

44-
self.assertEqual(list(six.iterkeys(actual_changes)), ['due'])
44+
self.assertEqual(list(actual_changes.keys()), ['due'])
4545

4646
# Use assertAlmostEqual to allow for millisecond loss when
4747
# converting to string in setUp
@@ -165,7 +165,7 @@ def test_composition(self):
165165
self.assertEqual(after_composition, expected_result)
166166

167167
def test_from_input(self):
168-
input_add_data = six.StringIO(
168+
input_add_data = io.StringIO(
169169
'{'
170170
'"description":"Go to Camelot",'
171171
'"entry":"20180618T030242Z",'
@@ -174,7 +174,7 @@ def test_from_input(self):
174174
'"uuid":"daa3ff05-f716-482e-bc35-3e1601e50778"'
175175
'}')
176176

177-
input_modify_data = six.StringIO(
177+
input_modify_data = io.StringIO(
178178
'\n'.join([
179179
input_add_data.getvalue(),
180180
(

Diff for: taskw/test/test_taskrc.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@
66
from taskw.fields import NumericField, ChoiceField
77

88

9-
if sys.version_info >= (2, 7):
10-
from unittest import TestCase
11-
else:
12-
from unittest2 import TestCase
9+
from unittest import TestCase
1310

1411

1512
class TestBasicLoading(TestCase):

Diff for: taskw/test/test_utils.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
import dateutil.tz
55
import pytz
6-
import six
76

87
from taskw.utils import (
98
convert_dict_to_override_args,
@@ -74,9 +73,7 @@ def test_with_backslashes(self):
7473

7574
def test_with_unicode(self):
7675
expected = {
77-
six.text_type('andthis'): (
78-
six.text_type('has a fucking \\backslash in it')
79-
)
76+
'andthis': 'has a fucking \\backslash in it'
8077
}
8178
line = r'[andthis:"has a fucking \\backslash in it"]'
8279
r = decode_task(line)

0 commit comments

Comments
 (0)