Skip to content

Commit

Permalink
Adds the simpleEnum class back. (#246)
Browse files Browse the repository at this point in the history
* Adds its meta class back without using six.
* Adds unit tests back
* Adds a deprecation warning for the classes.
  • Loading branch information
cheqianh committed Mar 17, 2023
1 parent d24da5c commit 614a93f
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 2 deletions.
104 changes: 103 additions & 1 deletion amazon/ion/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
import sys

from collections import namedtuple
from warnings import warn


class _RecordMetaClass(type):
"""Metaclass for defining named-tuple based immutable record types."""

def __new__(cls, name, bases, attrs):
if attrs.get('_record_sentinel') is None:
field_declarations = []
Expand Down Expand Up @@ -78,6 +80,7 @@ class MyRecord(record('a', ('b', 1))):
Args:
fields (list[str | (str, any)]): A sequence of str or pairs that
"""

class RecordType(object, metaclass=_RecordMetaClass):
_record_sentinel = True
_record_fields = fields
Expand All @@ -94,12 +97,14 @@ def coroutine(func):
Returns:
Callable: The decorated generator.
"""

def wrapper(*args, **kwargs):
gen = func(*args, **kwargs)
val = next(gen)
if val != None:
raise TypeError('Unexpected value from start of coroutine')
return gen

wrapper.__name__ = func.__name__
wrapper.__doc__ = func.__doc__
return wrapper
Expand Down Expand Up @@ -142,6 +147,7 @@ class CodePoint(int):
"""Evaluates as the ordinal of a code point, while also containing the unicode character representation and
indicating whether the code point was escaped.
"""

def __init__(self, *args, **kwargs):
self.char = None
self.is_escaped = False
Expand Down Expand Up @@ -193,6 +199,7 @@ def combine_surrogates():
real_code_point += (code_point - _HIGH_SURROGATE_START) << 10
real_code_point += (low_code_point - _LOW_SURROGATE_START)
return real_code_point, low_surrogate

try:
code_point, low = combine_surrogates()
except StopIteration:
Expand All @@ -217,15 +224,110 @@ def bit_length(value):
return 0
return len(bin(abs(value))) - 2


def total_seconds(td):
return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10 ** 6) / 10 ** 6
else:
def bit_length(value):
return value.bit_length()


def total_seconds(td):
return td.total_seconds()


bit_length.__doc__ = 'Returns the bit length of an integer'
total_seconds.__doc__ = 'Timedelta ``total_seconds`` with backported support in Python 2.6'


class _EnumMetaClass(type):
"""This is a deprecated, internal-only class in ion-python; do NOT use it for any reason.
Metaclass for simple enumerations.
Specifically provides the machinery necessary to emulate simplified Python 3.4 enumerations.
"""

def __init__(cls, name, bases, attrs):
members = {}
# Re-bind any non magic-named method with an instance of the enumeration.
for attr_name, attr_value in iter(attrs.items()):
if not attr_name.startswith('_') and not callable(attr_value) and not isinstance(attr_value, property):
if not isinstance(attr_value, int):
raise TypeError('Enum value must be an int: %r' % attr_value)
actual_value = cls(attr_name, attr_value)
setattr(cls, attr_name, actual_value)
members[attr_value] = actual_value

# Store the members reverse index.
cls._enum_members = members

type.__init__(cls, name, bases, attrs)

def __getitem__(cls, name):
"""Looks up an enumeration value field by integer value."""
return cls._enum_members[name]

def __iter__(self):
"""Iterates through the values of the enumeration in no specific order."""
return iter(self._enum_members.values())


class Enum(int, metaclass=_EnumMetaClass):
"""This is a deprecated, internal-only class in ion-python; do NOT use it for any reason
Simple integer based enumeration type.
Examples:
The typical declaration looks like::
class MyEnum(Enum):
A = 1
B = 2
C = 3
At this point ``MyEnum.A`` is an instance of ``MyEnum``.
Note:
Proper enumerations were added in Python 3.4 (PEP 435), this is a very simplified implementation
based loosely on that specification.
In particular, implicit order of the values is not supported.
Args:
value (int): the value associated with the enumeration.
Attributes:
name (str): The name of the enum.
value (int): The original value associated with the enum.
"""
_enum_members = {}

def __new__(cls, name, value):
return int.__new__(cls, value)

def __init__(self, name, value):
warn(f'{self.__class__.__name__} is an internal-only class in ion-python; do not use it for any reason. This '
f'class is deprecated and may be removed without further warning in any future release. Use `IntEnum` '
f'instead.',
DeprecationWarning, stacklevel=2)
super().__init__()
self.name = name
self.value = value

def __init_subclass__(cls, **kwargs):
warn(f'{cls.__name__} is an internal-only class in ion-python; do not use it for any reason. This '
f'class is deprecated and may be removed without further warning in any future release. Use `IntEnum` '
f'instead.',
DeprecationWarning, stacklevel=2)
super().__init_subclass__(**kwargs)

def __getnewargs__(self):
return self.name, self.value

def __str__(self):
return '<%s.%s: %s>' % (type(self).__name__, self.name, self.value)

__repr__ = __str__



54 changes: 54 additions & 0 deletions tests/test_util_enum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License").
# You may not use this file except in compliance with the License.
# A copy of the License is located at:
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
# OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the
# License.

import pytest

from amazon.ion.util import Enum


class SimpleEnum(Enum):
A = 1
B = 2


def test_enum_members():
assert SimpleEnum._enum_members == {1: SimpleEnum.A, 2: SimpleEnum.B}


def test_enum_reverse_lookup():
assert SimpleEnum[1] == SimpleEnum.A
assert SimpleEnum[2] == SimpleEnum.B


def test_enum_fields():
assert SimpleEnum.A.value == 1
assert SimpleEnum.A.name == 'A'
assert SimpleEnum.B.value == 2
assert SimpleEnum.B.name == 'B'

values = list(SimpleEnum)
values.sort()
assert values == [SimpleEnum.A, SimpleEnum.B]


def test_enum_as_int():
assert isinstance(SimpleEnum.A, int)
assert SimpleEnum.A == 1
assert SimpleEnum.A is not 1


def test_malformed_enum():
with pytest.raises(TypeError):
class BadEnum(Enum):
A = 'Allo'

0 comments on commit 614a93f

Please sign in to comment.