summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Behnel <stefan_ml@behnel.de>2020-06-19 09:22:39 +0200
committerStefan Behnel <stefan_ml@behnel.de>2020-06-19 09:22:39 +0200
commitd6fa2b351666f1070200fc0e887ce383ad2fe346 (patch)
treeda2e9d95f8e548e471d23583e6890678c0d1aaa7
parent4d928725ab76216bf72a02931496f28627784ace (diff)
parent1e72c2c57efbe0aac96b961a1ba1a8dee9ada4b7 (diff)
downloadcython-d6fa2b351666f1070200fc0e887ce383ad2fe346.tar.gz
Merge branch 'master' of git+ssh://github.com/cython/cython
-rw-r--r--Cython/Compiler/ExprNodes.py4
-rw-r--r--Cython/Compiler/ModuleNode.py22
-rw-r--r--Cython/Compiler/Nodes.py56
-rw-r--r--Cython/Compiler/Parsing.py49
-rw-r--r--Cython/Compiler/PyrexTypes.py69
-rw-r--r--Cython/Compiler/Symtab.py27
-rw-r--r--Cython/Utility/CpdefEnums.pyx18
-rw-r--r--docs/src/userguide/wrapping_CPlusPlus.rst27
-rw-r--r--tests/compile/cppenum.pyx31
-rw-r--r--tests/errors/cpp_enum_redeclare.pyx13
-rw-r--r--tests/errors/e_cenum_with_type.pyx8
-rw-r--r--tests/run/cpdef_scoped_enums.pyx20
-rw-r--r--tests/run/cpdef_scoped_enums_import.srctree71
-rw-r--r--tests/run/cpp_scoped_enums.pyx136
14 files changed, 514 insertions, 37 deletions
diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py
index d4e5fe970..0806a2923 100644
--- a/Cython/Compiler/ExprNodes.py
+++ b/Cython/Compiler/ExprNodes.py
@@ -2146,7 +2146,7 @@ class NameNode(AtomicExprNode):
entry = self.entry
if entry.is_type and entry.type.is_extension_type:
self.type_entry = entry
- if entry.is_type and entry.type.is_enum:
+ if entry.is_type and (entry.type.is_enum or entry.type.is_cpp_enum):
py_entry = Symtab.Entry(self.name, None, py_object_type)
py_entry.is_pyglobal = True
py_entry.scope = self.entry.scope
@@ -6957,7 +6957,7 @@ class AttributeNode(ExprNode):
ubcm_entry.is_unbound_cmethod = 1
ubcm_entry.scope = entry.scope
return self.as_name_node(env, ubcm_entry, target=False)
- elif type.is_enum:
+ elif type.is_enum or type.is_cpp_enum:
if self.attribute in type.values:
for entry in type.entry.enum_values:
if entry.name == self.attribute:
diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py
index 1e1774d54..2a2bfced5 100644
--- a/Cython/Compiler/ModuleNode.py
+++ b/Cython/Compiler/ModuleNode.py
@@ -155,7 +155,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.create_import_star_conversion_utility_code(env)
for name, entry in sorted(env.entries.items()):
if (entry.create_wrapper and entry.scope is env
- and entry.is_type and entry.type.is_enum):
+ and entry.is_type and (entry.type.is_enum or entry.type.is_cpp_enum)):
entry.type.create_type_wrapper(env)
def process_implementation(self, options, result):
@@ -880,7 +880,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
type = entry.type
if type.is_typedef: # Must test this first!
self.generate_typedef(entry, code)
- elif type.is_enum:
+ elif type.is_enum or type.is_cpp_enum:
self.generate_enum_definition(entry, code)
elif type.is_struct_or_union:
self.generate_struct_union_definition(entry, code)
@@ -957,8 +957,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("#endif")
code.putln(header)
var_entries = scope.var_entries
- if not var_entries:
- error(entry.pos, "Empty struct or union definition not allowed outside a 'cdef extern from' block")
for attr in var_entries:
code.putln(
"%s;" % attr.type.declaration_code(attr.cname))
@@ -1079,7 +1077,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.mark_pos(entry.pos)
type = entry.type
name = entry.cname or entry.name or ""
- header, footer = self.sue_header_footer(type, "enum", name)
+
+ kind = "enum class" if entry.type.is_cpp_enum else "enum"
+ header, footer = self.sue_header_footer(type, kind, name)
code.putln(header)
enum_values = entry.enum_values
if not enum_values:
@@ -1093,18 +1093,20 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
for value_entry in enum_values:
if value_entry.value_node is None:
- value_code = value_entry.cname
+ value_code = value_entry.cname.split("::")[-1]
else:
value_code = ("%s = %s" % (
- value_entry.cname,
+ value_entry.cname.split("::")[-1],
value_entry.value_node.result()))
if value_entry is not last_entry:
value_code += ","
code.putln(value_code)
code.putln(footer)
- if entry.type.typedef_flag:
- # Not pre-declared.
- code.putln("typedef enum %s %s;" % (name, name))
+
+ if entry.type.is_enum:
+ if entry.type.typedef_flag:
+ # Not pre-declared.
+ code.putln("typedef enum %s %s;" % (name, name))
def generate_typeobj_predeclaration(self, entry, code):
code.putln("")
diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py
index 6e9428a05..7f981b826 100644
--- a/Cython/Compiler/Nodes.py
+++ b/Cython/Compiler/Nodes.py
@@ -23,7 +23,7 @@ from . import TypeSlots
from .PyrexTypes import py_object_type, error_type
from .Symtab import (ModuleScope, LocalScope, ClosureScope, PropertyScope,
StructOrUnionScope, PyClassScope, CppClassScope, TemplateScope,
- punycodify_name)
+ CppScopedEnumScope, punycodify_name)
from .Code import UtilityCode
from .StringEncoding import EncodedString
from . import Future
@@ -1535,36 +1535,56 @@ class CppClassNode(CStructOrUnionDefNode, BlockNode):
class CEnumDefNode(StatNode):
- # name string or None
- # cname string or None
- # items [CEnumDefItemNode]
- # typedef_flag boolean
- # visibility "public" or "private" or "extern"
- # api boolean
- # in_pxd boolean
- # create_wrapper boolean
- # entry Entry
-
- child_attrs = ["items"]
+ # name string or None
+ # cname string or None
+ # scoped boolean Is a C++ scoped enum
+ # underlying_type CSimpleBaseTypeNode The underlying value type (int or C++ type)
+ # items [CEnumDefItemNode]
+ # typedef_flag boolean
+ # visibility "public" or "private" or "extern"
+ # api boolean
+ # in_pxd boolean
+ # create_wrapper boolean
+ # entry Entry
+
+ child_attrs = ["items", "underlying_type"]
def declare(self, env):
self.entry = env.declare_enum(
self.name, self.pos,
- cname=self.cname, typedef_flag=self.typedef_flag,
+ cname=self.cname,
+ scoped=self.scoped,
+ typedef_flag=self.typedef_flag,
visibility=self.visibility, api=self.api,
create_wrapper=self.create_wrapper)
def analyse_declarations(self, env):
+ scope = None
+ underlying_type = self.underlying_type.analyse(env)
+
+ if not underlying_type.is_int:
+ error(self.underlying_type.pos, "underlying type is not an integral type")
+
+ self.entry.type.underlying_type = underlying_type
+
+ if self.scoped and self.items is not None:
+ scope = CppScopedEnumScope(self.name, env)
+ scope.type = self.entry.type
+ else:
+ scope = env
+
if self.items is not None:
if self.in_pxd and not env.in_cinclude:
self.entry.defined_in_pxd = 1
for item in self.items:
- item.analyse_declarations(env, self.entry)
+ item.analyse_declarations(scope, self.entry)
def analyse_expressions(self, env):
return self
def generate_execution_code(self, code):
+ if self.scoped:
+ return # nothing to do here for C++ enums
if self.visibility == 'public' or self.api:
code.mark_pos(self.pos)
temp = code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=True)
@@ -1596,9 +1616,15 @@ class CEnumDefItemNode(StatNode):
if not self.value.type.is_int:
self.value = self.value.coerce_to(PyrexTypes.c_int_type, env)
self.value = self.value.analyse_const_expression(env)
+
+ if enum_entry.type.is_cpp_enum:
+ cname = "%s::%s" % (enum_entry.cname, self.name)
+ else:
+ cname = self.cname
+
entry = env.declare_const(
self.name, enum_entry.type,
- self.value, self.pos, cname=self.cname,
+ self.value, self.pos, cname=cname,
visibility=enum_entry.visibility, api=enum_entry.api,
create_wrapper=enum_entry.create_wrapper and enum_entry.name is None)
enum_entry.enum_values.append(entry)
diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py
index a5d8d57f9..4755f9ad1 100644
--- a/Cython/Compiler/Parsing.py
+++ b/Cython/Compiler/Parsing.py
@@ -3133,6 +3133,12 @@ def p_cdef_extern_block(s, pos, ctx):
def p_c_enum_definition(s, pos, ctx):
# s.sy == ident 'enum'
s.next()
+
+ scoped = False
+ if s.context.cpp and (s.sy == 'class' or (s.sy == 'IDENT' and s.systring == 'struct')):
+ scoped = True
+ s.next()
+
if s.sy == 'IDENT':
name = s.systring
s.next()
@@ -3140,24 +3146,49 @@ def p_c_enum_definition(s, pos, ctx):
if cname is None and ctx.namespace is not None:
cname = ctx.namespace + "::" + name
else:
- name = None
- cname = None
- items = None
+ name = cname = None
+ if scoped:
+ s.error("Unnamed scoped enum not allowed")
+
+ if scoped and s.sy == '(':
+ s.next()
+ underlying_type = p_c_base_type(s)
+ s.expect(')')
+ else:
+ underlying_type = Nodes.CSimpleBaseTypeNode(
+ pos,
+ name="int",
+ module_path = [],
+ is_basic_c_type = True,
+ signed = 1,
+ complex = 0,
+ longness = 0
+ )
+
s.expect(':')
items = []
+
if s.sy != 'NEWLINE':
p_c_enum_line(s, ctx, items)
else:
s.next() # 'NEWLINE'
s.expect_indent()
+
while s.sy not in ('DEDENT', 'EOF'):
p_c_enum_line(s, ctx, items)
+
s.expect_dedent()
+
+ if not items and ctx.visibility != "extern":
+ error(pos, "Empty enum definition not allowed outside a 'cdef extern from' block")
+
return Nodes.CEnumDefNode(
- pos, name = name, cname = cname, items = items,
- typedef_flag = ctx.typedef_flag, visibility = ctx.visibility,
- create_wrapper = ctx.overridable,
- api = ctx.api, in_pxd = ctx.level == 'module_pxd')
+ pos, name=name, cname=cname,
+ scoped=scoped, items=items,
+ underlying_type=underlying_type,
+ typedef_flag=ctx.typedef_flag, visibility=ctx.visibility,
+ create_wrapper=ctx.overridable,
+ api=ctx.api, in_pxd=ctx.level == 'module_pxd')
def p_c_enum_line(s, ctx, items):
if s.sy != 'pass':
@@ -3217,8 +3248,12 @@ def p_c_struct_or_union_definition(s, pos, ctx):
s.next()
s.expect_newline("Expected a newline")
s.expect_dedent()
+
+ if not attributes and ctx.visibility != "extern":
+ error(pos, "Empty struct or union definition not allowed outside a 'cdef extern from' block")
else:
s.expect_newline("Syntax error in struct or union definition")
+
return Nodes.CStructOrUnionDefNode(pos,
name = name, cname = cname, kind = kind, attributes = attributes,
typedef_flag = ctx.typedef_flag, visibility = ctx.visibility,
diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py
index 984868faf..b943fbe1e 100644
--- a/Cython/Compiler/PyrexTypes.py
+++ b/Cython/Compiler/PyrexTypes.py
@@ -182,6 +182,7 @@ class PyrexType(BaseType):
# is_struct_or_union boolean Is a C struct or union type
# is_struct boolean Is a C struct type
# is_enum boolean Is a C enum type
+ # is_cpp_enum boolean Is a C++ scoped enum type
# is_typedef boolean Is a typedef type
# is_string boolean Is a C char * type
# is_pyunicode_ptr boolean Is a C PyUNICODE * type
@@ -248,6 +249,7 @@ class PyrexType(BaseType):
is_cpp_string = 0
is_struct = 0
is_enum = 0
+ is_cpp_enum = False
is_typedef = 0
is_string = 0
is_pyunicode_ptr = 0
@@ -4019,6 +4021,73 @@ class CppClassType(CType):
if constructor is not None and best_match([], constructor.all_alternatives()) is None:
error(pos, "C++ class must have a nullary constructor to be %s" % msg)
+class CppScopedEnumType(CType):
+ # name string
+ # cname string
+
+ is_cpp_enum = True
+
+ def __init__(self, name, cname, underlying_type, namespace=None):
+ self.name = name
+ self.cname = cname
+ self.values = []
+ self.underlying_type = underlying_type
+ self.namespace = namespace
+
+ def __str__(self):
+ return self.name
+
+ def declaration_code(self, entity_code,
+ for_display=0, dll_linkage=None, pyrex=0):
+ if pyrex or for_display:
+ type_name = self.name
+ else:
+ if self.namespace:
+ type_name = "%s::%s" % (
+ self.namespace.empty_declaration_code(),
+ self.cname
+ )
+ else:
+ type_name = "enum %s" % self.cname
+ type_name = public_decl(type_name, dll_linkage)
+ return self.base_declaration_code(type_name, entity_code)
+
+ def create_from_py_utility_code(self, env):
+ if self.from_py_function:
+ return True
+ if self.underlying_type.create_from_py_utility_code(env):
+ self.from_py_function = '(%s)%s' % (
+ self.cname, self.underlying_type.from_py_function
+ )
+ return True
+
+ def create_to_py_utility_code(self, env):
+ if self.to_py_function is not None:
+ return True
+ if self.underlying_type.create_to_py_utility_code(env):
+ # Using a C++11 lambda here, which is fine since
+ # scoped enums are a C++11 feature
+ self.to_py_function = '[](const %s& x){return %s((%s)x);}' % (
+ self.cname,
+ self.underlying_type.to_py_function,
+ self.underlying_type.empty_declaration_code()
+ )
+ return True
+
+ def create_type_wrapper(self, env):
+ from .UtilityCode import CythonUtilityCode
+ rst = CythonUtilityCode.load(
+ "CppScopedEnumType", "CpdefEnums.pyx",
+ context={
+ "name": self.name,
+ "cname": self.cname.split("::")[-1],
+ "items": tuple(self.values),
+ "underlying_type": self.underlying_type.empty_declaration_code(),
+ },
+ outer_module_scope=env.global_scope())
+
+ env.use_utility_code(rst)
+
class TemplatePlaceholderType(CType):
diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py
index 081b4ec23..38d05e6cc 100644
--- a/Cython/Compiler/Symtab.py
+++ b/Cython/Compiler/Symtab.py
@@ -679,8 +679,8 @@ class Scope(object):
error(pos, "'%s' previously declared as '%s'" % (
entry.name, entry.visibility))
- def declare_enum(self, name, pos, cname, typedef_flag,
- visibility = 'private', api = 0, create_wrapper = 0):
+ def declare_enum(self, name, pos, cname, scoped, typedef_flag,
+ visibility='private', api=0, create_wrapper=0):
if name:
if not cname:
if (self.in_cinclude or visibility == 'public'
@@ -692,13 +692,18 @@ class Scope(object):
namespace = self.outer_scope.lookup(self.name).type
else:
namespace = None
- type = PyrexTypes.CEnumType(name, cname, typedef_flag, namespace)
+
+ if scoped:
+ type = PyrexTypes.CppScopedEnumType(name, cname, namespace)
+ else:
+ type = PyrexTypes.CEnumType(name, cname, typedef_flag, namespace)
else:
type = PyrexTypes.c_anon_enum_type
entry = self.declare_type(name, type, pos, cname = cname,
visibility = visibility, api = api)
entry.create_wrapper = create_wrapper
entry.enum_values = []
+
self.sue_entries.append(entry)
return entry
@@ -2628,6 +2633,22 @@ class CppClassScope(Scope):
return scope
+class CppScopedEnumScope(Scope):
+ # Namespace of a ScopedEnum
+
+ def __init__(self, name, outer_scope):
+ Scope.__init__(self, name, outer_scope, None)
+
+ def declare_var(self, name, type, pos,
+ cname=None, visibility='extern'):
+ # Add an entry for an attribute.
+ if not cname:
+ cname = name
+ entry = self.declare(name, cname, type, pos, visibility)
+ entry.is_variable = True
+ return entry
+
+
class PropertyScope(Scope):
# Scope holding the __get__, __set__ and __del__ methods for
# a property of an extension type.
diff --git a/Cython/Utility/CpdefEnums.pyx b/Cython/Utility/CpdefEnums.pyx
index 940027314..59ffad2d9 100644
--- a/Cython/Utility/CpdefEnums.pyx
+++ b/Cython/Utility/CpdefEnums.pyx
@@ -60,3 +60,21 @@ else:
{{for item in items}}
__Pyx_globals['{{item}}'] = {{name}}({{item}}, '{{item}}')
{{endfor}}
+
+#################### CppScopedEnumType ####################
+#@requires: EnumBase
+cdef dict __Pyx_globals = globals()
+
+if PY_VERSION_HEX >= 0x03040000:
+ # create new IntEnum()
+ __Pyx_globals["{{name}}"] = __Pyx_EnumBase('{{name}}', __Pyx_OrderedDict([
+ {{for item in items}}
+ ('{{item}}', <{{underlying_type}}>({{name}}.{{item}})),
+ {{endfor}}
+ ]))
+
+else:
+ __Pyx_globals["{{name}}"] = type('{{name}}', (__Pyx_EnumBase,), {})
+ {{for item in items}}
+ __Pyx_globals["{{name}}"](<{{underlying_type}}>({{name}}.{{item}}), '{{item}}')
+ {{endfor}}
diff --git a/docs/src/userguide/wrapping_CPlusPlus.rst b/docs/src/userguide/wrapping_CPlusPlus.rst
index fca022fbb..c0bb8f109 100644
--- a/docs/src/userguide/wrapping_CPlusPlus.rst
+++ b/docs/src/userguide/wrapping_CPlusPlus.rst
@@ -482,6 +482,33 @@ Note, however, that it is unnecessary to declare the arguments of extern
functions as references (const or otherwise) as it has no impact on the
caller's syntax.
+Scoped Enumerations
+-------------------
+
+Cython supports scoped enumerations (:keyword:`enum class`) in C++ mode::
+
+ cdef enum class Cheese:
+ cheddar = 1
+ camembert = 2
+
+As with "plain" enums, you may access the enumerators as attributes of the type.
+Unlike plain enums however, the enumerators are not visible to the
+enclosing scope::
+
+ cdef Cheese c1 = Cheese.cheddar # OK
+ cdef Cheese c2 = cheddar # ERROR!
+
+Optionally, you may specify the underlying type of a scoped enumeration.
+This is especially important when declaring an external scoped enumeration
+with an underlying type::
+
+ cdef extern from "Foo.h":
+ cdef enum class Spam(unsigned int):
+ x = 10
+ y = 20
+ ...
+
+Declaring an enum class as ``cpdef`` will create a :pep:`435`-style Python wrapper.
``auto`` Keyword
----------------
diff --git a/tests/compile/cppenum.pyx b/tests/compile/cppenum.pyx
new file mode 100644
index 000000000..8431ac83b
--- /dev/null
+++ b/tests/compile/cppenum.pyx
@@ -0,0 +1,31 @@
+# mode: compile
+# tag: cpp,cpp11
+
+
+cpdef enum class Spam:
+ a, b
+ c
+ d
+ e
+ f = 42
+
+
+cpdef enum class Cheese(unsigned int):
+ x = 1
+ y = 2
+
+
+cdef enum struct parrot_state:
+ alive = 1
+ dead = 0
+
+
+cdef void eggs():
+ cdef Spam s1
+ s1 = Spam.a
+ s2 = Spam.b
+
+ cdef Cheese c1
+ c1 = Cheese.x
+
+eggs()
diff --git a/tests/errors/cpp_enum_redeclare.pyx b/tests/errors/cpp_enum_redeclare.pyx
new file mode 100644
index 000000000..3d2ae7763
--- /dev/null
+++ b/tests/errors/cpp_enum_redeclare.pyx
@@ -0,0 +1,13 @@
+# mode: error
+# tag: cpp
+
+cdef enum class Spam:
+ a
+
+cdef enum class Spam:
+ b
+
+_ERRORS="""
+7:5: 'Spam' redeclared
+4:5: Previous declaration is here
+"""
diff --git a/tests/errors/e_cenum_with_type.pyx b/tests/errors/e_cenum_with_type.pyx
new file mode 100644
index 000000000..43c0e08fc
--- /dev/null
+++ b/tests/errors/e_cenum_with_type.pyx
@@ -0,0 +1,8 @@
+# mode: error
+
+cdef enum Spam(int):
+ a, b
+
+_ERRORS = u"""
+3:14: Expected ':', found '('
+"""
diff --git a/tests/run/cpdef_scoped_enums.pyx b/tests/run/cpdef_scoped_enums.pyx
new file mode 100644
index 000000000..796e0fd3c
--- /dev/null
+++ b/tests/run/cpdef_scoped_enums.pyx
@@ -0,0 +1,20 @@
+# mode: run
+# tag: cpp, cpp11
+
+cdef extern from *:
+ """
+ enum class Enum1 {
+ Item1 = 1,
+ Item2 = 2
+ };
+ """
+ cpdef enum class Enum1:
+ Item1
+ Item2
+
+
+def test_enum_to_list():
+ """
+ >>> test_enum_to_list()
+ """
+ assert list(Enum1) == [1, 2]
diff --git a/tests/run/cpdef_scoped_enums_import.srctree b/tests/run/cpdef_scoped_enums_import.srctree
new file mode 100644
index 000000000..2d79fb05a
--- /dev/null
+++ b/tests/run/cpdef_scoped_enums_import.srctree
@@ -0,0 +1,71 @@
+# mode: run
+# tag: cpp, cpp11
+
+"""
+PYTHON setup.py build_ext --inplace
+PYTHON -c "import runner"
+"""
+
+######## setup.py ########
+
+from Cython.Build.Dependencies import cythonize
+from distutils.core import setup
+setup(ext_modules=cythonize("*.pyx", language='c++'))
+
+setup(
+ ext_modules = cythonize([
+ "cheese.pyx",
+ "import_scoped_enum_test.pyx",
+ "dotted_import_scoped_enum_test.pyx"
+ ])
+)
+
+######## cheese.pxd ########
+# distutils: language = c++
+# distutils: extra_compile_args = -std=c++11
+
+
+cdef extern from * namespace "Namespace":
+ """
+ namespace Namespace {
+ enum class Cheese {
+ cheddar = 1,
+ camembert = 2
+ };
+ }
+ """
+ cpdef enum class Cheese:
+ cheddar
+ camembert
+
+######## cheese.pyx ########
+# distutils: language = c++
+# distutils: extra_compile_args = -std=c++11
+
+pass
+
+######## import_scoped_enum_test.pyx ########
+# distutils: language = c++
+# distutils: extra_compile_args = -std=c++11
+
+from cheese import Cheese
+from cheese cimport Cheese
+
+cdef Cheese c = Cheese.cheddar
+assert list(Cheese) == [1, 2]
+
+######## dotted_import_scoped_enum_test.pyx ########
+# distutils: language = c++
+# distutils: extra_compile_args = -std=c++11
+
+
+cimport cheese
+
+cdef cheese.Cheese c = cheese.Cheese.cheddar
+assert [cheese.Cheese.cheddar, cheese.Cheese.camembert] == [1, 2]
+cdef cheese.Cheese d = int(1)
+
+######## runner.py ########
+
+import import_scoped_enum_test
+import dotted_import_scoped_enum_test
diff --git a/tests/run/cpp_scoped_enums.pyx b/tests/run/cpp_scoped_enums.pyx
new file mode 100644
index 000000000..7a2bc912f
--- /dev/null
+++ b/tests/run/cpp_scoped_enums.pyx
@@ -0,0 +1,136 @@
+# mode: run
+# tag: cpp, cpp11
+
+from libcpp.limits cimport numeric_limits
+
+cdef extern from *:
+ """
+ enum class Enum1 {
+ Item1,
+ Item2
+ };
+ """
+ cdef enum class Enum1:
+ Item1
+ Item2
+
+
+cdef extern from * namespace "Namespace1":
+ """
+ namespace Namespace1 {
+ enum class Enum2 {
+ Item1,
+ Item2
+ };
+ }
+ """
+ cdef enum class Enum2:
+ Item1
+ Item2
+
+
+cdef enum class Enum3(int):
+ a = 1
+ b = 2
+
+
+cdef extern from *:
+ """
+ enum class sorted
+ {
+ a = 1,
+ b = 0
+ };
+ """
+ cdef enum class Enum4 "sorted":
+ a
+ b
+
+
+cdef extern from *:
+ """
+ #include <limits>
+
+ enum class LongIntEnum : long int {
+ val = std::numeric_limits<long int>::max(),
+ };
+ """
+ enum class LongIntEnum(long int):
+ val
+
+
+def test_compare_enums():
+ """
+ >>> test_compare_enums()
+ (True, True, False, False)
+ """
+ cdef Enum1 x, y
+ x = Enum1.Item1
+ y = Enum1.Item2
+
+ return (
+ x == Enum1.Item1,
+ y == Enum1.Item2,
+ x == Enum1.Item2,
+ y == Enum1.Item1
+ )
+
+
+def test_compare_namespace_enums():
+ """
+ >>> test_compare_enums()
+ (True, True, False, False)
+ """
+ cdef Enum2 z, w
+
+ z = Enum2.Item1
+ w = Enum2.Item2
+
+ return (
+ z == Enum2.Item1,
+ w == Enum2.Item2,
+ z == Enum2.Item2,
+ w == Enum2.Item1
+ )
+
+
+def test_coerce_to_from_py_value(object i):
+ """
+ >>> test_coerce_to_from_py_value(1)
+ (True, False)
+
+ >>> test_coerce_to_from_py_value(2)
+ (False, True)
+
+ >>> test_coerce_to_from_py_value(3)
+ (False, False)
+
+ >>> test_coerce_to_from_py_value(11111111111111111111111111111111111111111111)
+ Traceback (most recent call last):
+ OverflowError: Python int too large to convert to C long
+ """
+ cdef Enum3 x = i
+ y = Enum3.b
+
+ return (
+ x == Enum3.a,
+ y == int(i)
+ )
+
+
+def test_reserved_cname():
+ """
+ >>> test_reserved_cname()
+ True
+ """
+ cdef Enum4 x = Enum4.a
+ return Enum4.a == int(1)
+
+
+def test_large_enum():
+ """
+ >>> test_large_enum()
+ True
+ """
+ long_max = int(numeric_limits[long].max())
+ return LongIntEnum.val == long_max