Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds the simpleEnum class back. #246

Merged
merged 4 commits into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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__



2 changes: 1 addition & 1 deletion ion-c
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's revert the submodule update and address that in a separate PR, since it introduced errors that haven't yet been diagnosed.

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'