Skip to content
This repository has been archived by the owner on Apr 10, 2023. It is now read-only.

Commit

Permalink
Drop Python 2 and Python 3.5 support
Browse files Browse the repository at this point in the history
  • Loading branch information
rossmacarthur committed Nov 27, 2020
1 parent 7166274 commit 453b7e6
Show file tree
Hide file tree
Showing 18 changed files with 34 additions and 183 deletions.
7 changes: 3 additions & 4 deletions .github/workflows/build.yaml
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [2.7, pypy2, 3.5, 3.6, 3.7, 3.8, 3.9, pypy3]
python-version: [3.6, 3.7, 3.8, 3.9, pypy3]

steps:
- uses: actions/checkout@v2
Expand All @@ -23,14 +23,13 @@ jobs:
run: just install-dev

- name: Lint
if: matrix.python-version == 3.7
if: matrix.python-version != 'pypy3'
run: just lint

- name: Test
run: just test

- uses: codecov/codecov-action@v1
if: matrix.python-version == 3.7

deploy:
needs: test
Expand All @@ -44,7 +43,7 @@ jobs:

- uses: actions/setup-python@v2
with:
python-version: 3.7
python-version: 3.8

- name: Check tag against package version
run: grep '__version__' src/serde/__init__.py | grep -q "'${GITHUB_REF#refs/tags/}'"
Expand Down
14 changes: 1 addition & 13 deletions Justfile
Expand Up @@ -2,14 +2,6 @@
help:
@just --list

# Run any Just command but only if we are using the given Python version.
_python VERSION COMMAND +ARGS='':
@if python \
-c "import platform as p;print ('{} {}'.format(p.python_version(), p.python_implementation()))" \
| grep -q "^{{ VERSION }}"; then \
just {{ COMMAND }} {{ ARGS }}; \
fi

# Completely removing anything not tracked by Git.
pristine:
git reset --hard && git clean -dfx
Expand Down Expand Up @@ -38,13 +30,9 @@ fmt:
black .
isort .

_test +ARGS='':
pytest -xvv --cov=serde --cov-report xml --cov-report term-missing {{ ARGS }} tests

# Run all tests.
test:
@just _python '\(2\|3.5\)' _test
@just _python 3.[6-9] _test --cov-fail-under 100
pytest -xvv --cov=serde --cov-report xml --cov-report term-missing --cov-fail-under 100 tests

# Compile docs.
docs:
Expand Down
29 changes: 0 additions & 29 deletions README.rst
Expand Up @@ -452,35 +452,6 @@ can always subclass a ``Field`` and override the relevant methods.
... super().validate(value)
... validators.Between(0.0, 100.0)(value)
Python 2.7 and Python 3.5 compatibility
---------------------------------------

Class annotations were only added in Python 3.6, for this reason class
attributes can be used for ``Field`` definitions for projects that require
compatibility for these versions. For example

.. code-block:: python
class Artist(Model):
name: fields.Str()
class Album(Model):
title: fields.Str()
release_date: fields.Optional(fields.Date)
artist: fields.Nested(Artist)
is equivalent to

.. code-block:: python
class Artist(Model):
name = fields.Str()
class Album(Model):
title = fields.Str()
release_date = fields.Optional(fields.Date)
artist = fields.Nested(Artist)
Model states and processes
--------------------------

Expand Down
8 changes: 4 additions & 4 deletions dev-requirements.in
@@ -1,15 +1,15 @@
# Linting requirements
black; python_version>="3.6" and implementation_name=="cpython"
black; implementation_name=="cpython"
flake8
flake8-bugbear; python_version>="3"
flake8-bugbear
flake8-comprehensions
flake8-expression-complexity; python_version>="3.6"
flake8-expression-complexity
flake8-mock
flake8-mutable
flake8-pep3101
flake8-pytest
flake8-quotes
isort; python_version>="3.6" and implementation_name=="cpython"
isort; implementation_name=="cpython"
pep8-naming

# Testing requirements
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Expand Up @@ -4,4 +4,4 @@ lines_after_imports = 2

[tool.black]
skip-string-normalization = true
target-version = ['py27']
target-version = ['py36']
5 changes: 0 additions & 5 deletions setup.cfg
@@ -1,8 +1,3 @@
[bdist_wheel]
universal = 1

[flake8]
ignore = C408,W503
max-line-length = 100
per-file-ignores =
tests/test_model_py36.py: E999
10 changes: 3 additions & 7 deletions setup.py
Expand Up @@ -43,14 +43,14 @@ def get_metadata():
metadata = get_metadata()

# Primary requirements
install_requires = ['isodate==0.6.*', 'six==1.*,>=1.13.0']
install_requires = ['isodate==0.6.*']
ext_requires = ['chardet==3.*', 'validators>=0.12.0']

setup(
# Options
install_requires=install_requires,
extras_require={'ext': ext_requires},
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*',
python_requires='>=3.6',
packages=find_packages('src'),
package_dir={'': 'src'},
# Metadata
Expand All @@ -65,10 +65,6 @@ def get_metadata():
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
Expand All @@ -77,5 +73,5 @@ def get_metadata():
'Programming Language :: Python :: Implementation :: PyPy',
],
keywords='serde serialization deserialization validation schema json',
**metadata
**metadata,
)
4 changes: 1 addition & 3 deletions src/serde/exceptions.py
Expand Up @@ -4,8 +4,6 @@

from contextlib import contextmanager

import six


__all__ = [
'ContextError',
Expand Down Expand Up @@ -79,7 +77,7 @@ def messages(self):
# Avoids tags which might not have `_serde_name`
if isinstance(field, Field):
d = {field._serde_name: d}
elif isinstance(field, (six.string_types, six.integer_types)):
elif isinstance(field, (str, int)):
d = {field: d}
return d

Expand Down
53 changes: 11 additions & 42 deletions src/serde/fields.py
Expand Up @@ -6,10 +6,9 @@
import datetime
import re
import uuid
from collections.abc import Mapping as MappingType

import isodate
from six import PY3, binary_type, integer_types, text_type
from six.moves.collections_abc import Mapping as MappingType

from serde.exceptions import ContextError, ValidationError, add_context
from serde.utils import is_subclass, try_lookup, zip_equal
Expand Down Expand Up @@ -571,7 +570,7 @@ def serialize(self, value):
"""
value = self.ty(
(self._apply('_serialize', element) for element in self._iter(value)),
**self.kwargs
**self.kwargs,
)
return super(_Container, self).serialize(value)

Expand All @@ -585,7 +584,7 @@ def deserialize(self, value):
value = super(_Container, self).deserialize(value)
return self.ty(
(self._apply('_deserialize', element) for element in self._iter(value)),
**self.kwargs
**self.kwargs,
)

def normalize(self, value):
Expand All @@ -598,7 +597,7 @@ def normalize(self, value):
value = super(_Container, self).normalize(value)
return self.ty(
(self._apply('_normalize', element) for element in self._iter(value)),
**self.kwargs
**self.kwargs,
)

def validate(self, value):
Expand Down Expand Up @@ -859,22 +858,7 @@ def __init__(self, **kwargs): # noqa: N807
Float = create_primitive('Float', float)
Int = create_primitive('Int', int)
Str = create_primitive('Str', str)
Bytes = create_primitive('Bytes', bytes) if bytes != str else Str

try:
BaseString = create_primitive('BaseString', basestring)
except NameError:
pass

try:
Long = create_primitive('Long', long)
except NameError:
pass

try:
Unicode = create_primitive('Unicode', unicode)
except NameError:
pass
Bytes = create_primitive('Bytes', bytes)

del create_primitive

Expand Down Expand Up @@ -1064,7 +1048,7 @@ def __init__(self, encoding=None, errors='strict', **kwargs):
"""
Create a new `Text`.
"""
super(Text, self).__init__(text_type, **kwargs)
super(Text, self).__init__(str, **kwargs)
self.encoding = encoding
self.errors = errors
if self.encoding is None:
Expand All @@ -1074,7 +1058,7 @@ def normalize(self, value):
"""
Normalize byte strings to unicode strings.
"""
if isinstance(value, binary_type):
if isinstance(value, bytes):
if self.encoding is None:
value = value.decode(
encoding=self._detect(value)['encoding'], errors=self.errors
Expand Down Expand Up @@ -1159,11 +1143,11 @@ def normalize(self, value):
"""
if not isinstance(value, uuid.UUID):
input_form = None
if isinstance(value, text_type):
if isinstance(value, str):
input_form = 'hex'
elif isinstance(value, binary_type):
input_form = 'bytes' if PY3 or len(value) == 16 else 'hex'
elif isinstance(value, integer_types):
elif isinstance(value, bytes):
input_form = 'bytes'
elif isinstance(value, int):
input_form = 'int'
elif isinstance(value, (list, tuple)):
input_form = 'fields'
Expand Down Expand Up @@ -1277,19 +1261,4 @@ def validate(self, value):
uuid.UUID: Uuid,
}

try:
_FIELD_CLASS_MAP[basestring] = BaseString
except NameError:
pass

try:
_FIELD_CLASS_MAP[long] = Long
except NameError:
pass

try:
_FIELD_CLASS_MAP[unicode] = Unicode
except NameError:
pass

__all__ = [name for name, obj in globals().items() if is_subclass(obj, Field)]
7 changes: 2 additions & 5 deletions src/serde/model.py
Expand Up @@ -6,8 +6,6 @@
import json
from collections import OrderedDict

from six import add_metaclass

from serde.exceptions import ContextError, add_context
from serde.fields import Field, _resolve
from serde.utils import dict_partition, zip_until_right
Expand Down Expand Up @@ -158,8 +156,7 @@ def __tags__(cls):
return cls._tags[:]


@add_metaclass(ModelType)
class Model(object):
class Model(object, metaclass=ModelType):
"""
The base model.
"""
Expand Down Expand Up @@ -236,7 +233,7 @@ def __repr__(self):
"""
return '<{module}.{name} model at 0x{id:x}>'.format(
module=self.__class__.__module__,
name=getattr(self.__class__, '__qualname__', self.__class__.__name__),
name=self.__class__.__qualname__,
id=id(self),
)

Expand Down
3 changes: 1 addition & 2 deletions src/serde/tags.py
Expand Up @@ -58,8 +58,7 @@ def lookup_tag(self, variant):
Returns:
str: the corresponding tag value.
"""
name = getattr(variant, '__qualname__', variant.__name__)
return '{}.{}'.format(variant.__module__, name)
return '{}.{}'.format(variant.__module__, variant.__qualname__)

def lookup_variant(self, tag):
"""
Expand Down
3 changes: 1 addition & 2 deletions src/serde/utils.py
Expand Up @@ -4,8 +4,7 @@

import importlib
from collections import OrderedDict

from six.moves import zip_longest
from itertools import zip_longest

from serde.exceptions import MissingDependency

Expand Down
36 changes: 0 additions & 36 deletions tests/__init__.py
@@ -1,40 +1,4 @@
import os
import sys

import mock
import six

from serde import fields


REPO_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))


def py2_patch_str_with_basestring(f):
def decorated_function(*args, **kwargs):
if six.PY2:
with mock.patch.dict(
'serde.fields._FIELD_CLASS_MAP', {str: fields.BaseString}
):
return f(*args, **kwargs)
return f(*args, **kwargs)

return decorated_function


def py3(f):
def decorated_function(*args, **kwargs):
if six.PY2:
return
return f(*args, **kwargs)

return decorated_function


def py36(f):
def decorated_function(*args, **kwargs):
if sys.version_info[:2] < (3, 6):
return
return f(*args, **kwargs)

return decorated_function
5 changes: 0 additions & 5 deletions tests/conftest.py

This file was deleted.

0 comments on commit 453b7e6

Please sign in to comment.