Skip to content

Commit ae42b7e

Browse files
committed
Phase 2 csp.Enum deprecation - deleted c++ side PyCspEnum / PyCspEnumMeta all together. IntEnum is now the only 'valid' native csp type. wrote csp.Enum type that derives from IntEnum for backward compatibility
Signed-off-by: Rob Ambalu <robert.ambalu@point72.com>
1 parent 4198e7d commit ae42b7e

13 files changed

Lines changed: 119 additions & 440 deletions

cpp/csp/python/Conversions.h

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -446,20 +446,7 @@ inline PyObject * toPython( const CspEnum & e, const CspType & type )
446446
auto & enumType = static_cast<const CspEnumType&>( type );
447447
const auto * emeta = static_cast<const DialectCspEnumMeta*>( enumType.meta().get() );
448448

449-
if( emeta -> isPyIntEnum() )
450-
{
451-
//TODO - precache IntEnums on DialectCspEnumMeta instead of PyCspEnumMeta for faster IntEnum conversion
452-
PyObjectPtr val = PyObjectPtr::own( toPython( e.value() ) );
453-
PyObject * obj = PyObject_CallOneArg( ( PyObject * ) emeta -> pyType().get(), val.get() );
454-
if( !obj )
455-
CSP_THROW( PythonPassthrough, "" );
456-
return obj;
457-
}
458-
459-
PyObject * obj = emeta -> pyMeta() -> toPyEnum( e );
460-
if( !obj ) [[unlikely]]
461-
CSP_THROW( ValueError, e.value() << " is not a valid value on csp.enum type " << emeta -> name() );
462-
return obj;
449+
return emeta -> toPyEnum( e.value() );
463450
}
464451

465452
template<>
@@ -473,10 +460,7 @@ inline CspEnum fromPython( PyObject * o, const CspType & type )
473460
if( !PyObject_IsInstance( o, ( PyObject * ) emeta -> pyType().get() ) )
474461
CSP_THROW( TypeError, "Invalid enum type, expected enum type " << emeta -> pyType() -> tp_name << " got " << Py_TYPE( o ) -> tp_name );
475462

476-
if( emeta -> isPyIntEnum() )
477-
return static_cast<const CspEnumType &>( type ).meta() -> create( PyLong_AsLong( o ) );
478-
479-
return static_cast<PyCspEnum *>( o ) -> enum_;
463+
return static_cast<const CspEnumType &>( type ).meta() -> create( PyLong_AsLong( o ) );
480464
}
481465

482466
//TimeDelta

cpp/csp/python/CspTypeFactory.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,20 @@ namespace csp::python
1010
CspTypeFactory::CspTypeFactory()
1111
{
1212
PyObject *enum_mod = PyImport_ImportModule( "enum" );
13-
m_intEnumPyType = PyTypeObjectPtr::own( ( PyTypeObject * ) PyObject_GetAttrString( enum_mod, "IntEnum" ) );
13+
m_intEnumPyType = ( PyTypeObject * ) PyObject_GetAttrString( enum_mod, "IntEnum" );
1414
}
1515

1616
CspTypeFactory & CspTypeFactory::instance()
1717
{
18-
static CspTypeFactory s_instance;
19-
return s_instance;
18+
//We let this leak since some csp types ( ie CspEnum ) can hold a ref to DialectCspEnumMeta which holds Ptrs
19+
//to python objects, which cant be destroyed statically after python interpreter is shutdown
20+
static CspTypeFactory * s_instance = new CspTypeFactory();
21+
return *s_instance;
22+
}
23+
24+
bool CspTypeFactory::isCspEnumPyType( PyTypeObject * pyType )
25+
{
26+
return PyType_IsSubtype( pyType, m_intEnumPyType );
2027
}
2128

2229
CspTypePtr & CspTypeFactory::typeFromPyType( PyObject * pyTypeObj )
@@ -65,12 +72,7 @@ CspTypePtr & CspTypeFactory::typeFromPyType( PyObject * pyTypeObj )
6572
auto meta = ( ( PyStructMeta * ) pyType ) -> structMeta;
6673
rv.first -> second = std::make_shared<csp::CspStructType>( meta );
6774
}
68-
else if( PyType_IsSubtype( pyType, &PyCspEnum::PyType ) )
69-
{
70-
auto meta = ( ( PyCspEnumMeta * ) pyType ) -> enumMeta;
71-
rv.first -> second = std::make_shared<csp::CspEnumType>( meta );
72-
}
73-
else if( PyType_IsSubtype( pyType, m_intEnumPyType.get() ) )
75+
else if( isCspEnumPyType( pyType ) )
7476
{
7577
auto meta = createCspEnumMetaFromIntEnum( PyTypeObjectPtr::incref( pyType ) );
7678
rv.first -> second = std::make_shared<csp::CspEnumType>( meta );

cpp/csp/python/CspTypeFactory.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class CSPTYPESIMPL_EXPORT CspTypeFactory
1818
CspTypePtr & typeFromPyType( PyObject * );
1919
void removeCachedType( PyTypeObject * );
2020

21-
PyTypeObject * intEnumPyType() { return m_intEnumPyType.get(); }
21+
bool isCspEnumPyType( PyTypeObject * pyType );
2222

2323
private:
2424
using Cache = std::unordered_map<PyTypeObject *, CspTypePtr>;
@@ -28,7 +28,7 @@ class CSPTYPESIMPL_EXPORT CspTypeFactory
2828
CspTypeFactory();
2929
Cache m_cache;
3030

31-
PyTypeObjectPtr m_intEnumPyType;
31+
PyTypeObject * m_intEnumPyType;
3232
};
3333

3434
}

cpp/csp/python/PyCspEnum.cpp

Lines changed: 11 additions & 257 deletions
Original file line numberDiff line numberDiff line change
@@ -12,270 +12,24 @@ DialectCspEnumMeta::DialectCspEnumMeta( PyTypeObjectPtr pyType, const std::strin
1212
CspEnumMeta( name, def ),
1313
m_pyType( pyType )
1414
{
15-
m_isPyIntEnum = PyType_IsSubtype( pyType.get(), CspTypeFactory::instance().intEnumPyType() );
16-
}
17-
18-
/*
19-
MetaClass Madness NOTES!!! -- see PyStruct.cpp for note, same idea
20-
*/
21-
22-
static PyObject * PyCspEnumMeta_new( PyTypeObject *subtype, PyObject *args, PyObject *kwds )
23-
{
24-
CSP_BEGIN_METHOD;
25-
26-
PyObject * pyname;
27-
PyObject * bases;
28-
PyObject * dict;
29-
if( !PyArg_ParseTuple( args, "UO!O!",
30-
&pyname,
31-
&PyTuple_Type, &bases,
32-
&PyDict_Type, &dict ) )
33-
CSP_THROW( PythonPassthrough, "" );
34-
35-
//subtype is python defined CspEnumMeta class
36-
PyCspEnumMeta * pymeta = ( PyCspEnumMeta * ) PyType_Type.tp_new( subtype, args, kwds );
37-
38-
//Note that we call ctor without parents so as not to 0-init the base POD PyTypeObject class after its been initialized
39-
new ( pymeta ) PyCspEnumMeta;
40-
41-
//this would be the CspEnum class on python side, it doesnt create any metadata for itself
42-
if( pymeta -> ht_type.tp_base == &PyCspEnum::PyType )
43-
return ( PyObject * ) pymeta;
44-
45-
std::string name = PyUnicode_AsUTF8( pyname );
46-
47-
PyObject * metadata = PyDict_GetItemString( dict, "__metadata__" );
48-
if( !metadata )
49-
CSP_THROW( KeyError, "CspEnumMeta missing __metadata__" );
50-
51-
CspEnumMeta::ValueDef def;
52-
53-
{
54-
PyObject *key, *value;
55-
Py_ssize_t pos = 0;
56-
while( PyDict_Next( metadata, &pos, &key, &value ) )
57-
{
58-
const char * keystr = PyUnicode_AsUTF8( key );
59-
if( !keystr )
60-
CSP_THROW( PythonPassthrough, "" );
61-
62-
if( !PyLong_Check( value ) )
63-
CSP_THROW( TypeError, "csp.Enum key " << keystr << " expected an integer got " << PyObjectPtr::incref( value ) );
64-
65-
def[ keystr ] = fromPython<int64_t>( value );
66-
}
67-
}
68-
69-
//back reference to the csp enum type that will be accessible on the csp enum -> meta()
70-
//intentionally dont incref here to break the circular dep of type -> shared_ptr on CspEnumMeta
71-
PyTypeObjectPtr typePtr = PyTypeObjectPtr::own( ( PyTypeObject * ) pymeta );
72-
auto enumMeta = std::make_shared<DialectCspEnumMeta>( typePtr, name, def );
73-
74-
pymeta -> enumMeta = enumMeta;
75-
7615
//pre-create instances
77-
pymeta -> enumsByName = PyObjectPtr::own( PyDict_New() );
78-
pymeta -> enumsByValue = PyObjectPtr::own( PyDict_New() );
16+
m_enumsByName = PyObjectPtr::own( PyDict_New() );
7917

80-
for( auto & [ key, value ] : def )
18+
PyObjectPtr iter = PyObjectPtr::check( PyObject_GetIter( ( PyObject * ) pyType.get() ) );
19+
PyObject * member;
20+
while( ( member = PyIter_Next( iter.get() ) ) != NULL )
8121
{
82-
PyCspEnum * enum_ = ( PyCspEnum * ) ( (PyTypeObject * ) pymeta ) -> tp_alloc( (PyTypeObject * ) pymeta, 0 );
22+
PyObjectPtr name = PyObjectPtr::check( PyObject_GetAttrString( member, "name" ) );
8323

84-
new( enum_ ) PyCspEnum( enumMeta -> create( value ) );
85-
enum_ -> enumName = PyObjectPtr::own( toPython( key ) );
86-
enum_ -> enumValue = PyObjectPtr::own( toPython<int64_t>( value ) );
24+
if( !PyLong_Check( member ) )
25+
CSP_THROW( TypeError, "enum key " << name << " expected an integer got " << PyObjectPtr::incref( member ) );
26+
27+
int64_t value = fromPython<int64_t>( member );
28+
m_enumsByCValue[ value ] = PyObjectPtr::incref( member );
8729

88-
pymeta -> enumsByCValue[ value ] = PyObjectPtr::incref( enum_ );
89-
90-
if( PyDict_SetItem( pymeta -> enumsByName.get(), enum_ -> enumName.get(), enum_ ) < 0 )
91-
CSP_THROW( PythonPassthrough, "" );
92-
93-
if( PyDict_SetItem( pymeta -> enumsByValue.get(), enum_ -> enumValue.get(), enum_ ) < 0 )
94-
CSP_THROW( PythonPassthrough, "" );
95-
96-
//We also have to update the items in the actual type's dict so FooEnum.A is a PyCspEnum!
97-
if( PyDict_SetItem( ( ( PyTypeObject * ) pymeta ) -> tp_dict, enum_ -> enumName.get(), enum_ ) < 0 )
30+
if( PyDict_SetItem( m_enumsByName.get(), name.get(), member ) < 0 )
9831
CSP_THROW( PythonPassthrough, "" );
9932
}
100-
101-
return ( PyObject * ) pymeta;
102-
CSP_RETURN_NULL;
103-
}
104-
105-
PyObject * PyCspEnumMeta::toPyEnum( CspEnum e ) const
106-
{
107-
auto it = enumsByCValue.find( e.value() );
108-
if( it == enumsByCValue.end() )
109-
return nullptr;
110-
111-
PyObject * rv = it -> second.get();
112-
Py_INCREF( rv );
113-
return rv;
114-
}
115-
116-
void PyCspEnumMeta_dealloc( PyCspEnumMeta * m )
117-
{
118-
CspTypeFactory::instance().removeCachedType( reinterpret_cast<PyTypeObject*>( m ) );
119-
m -> ~PyCspEnumMeta();
120-
PyCspEnumMeta::PyType.tp_free( m );
121-
}
122-
123-
PyObject * PyCspEnumMeta_subscript( PyCspEnumMeta * self, PyObject * key )
124-
{
125-
CSP_BEGIN_METHOD;
126-
127-
PyObject * obj = PyDict_GetItem( self -> enumsByName.get(), key );
128-
129-
if( !obj )
130-
CSP_THROW( ValueError, PyObjectPtr::incref( key ) << " is not a valid name on csp.enum type " << ( ( PyTypeObject * ) self ) -> tp_name );
131-
132-
Py_INCREF( obj );
133-
return obj;
134-
CSP_RETURN_NULL;
135-
}
136-
137-
138-
static PyMappingMethods PyCspEnumMeta_MappingMethods = {
139-
0, /*mp_length */
140-
(binaryfunc) PyCspEnumMeta_subscript, /*mp_subscript */
141-
};
142-
143-
PyTypeObject PyCspEnumMeta::PyType = {
144-
PyVarObject_HEAD_INIT(nullptr, 0)
145-
"_cspimpl.PyCspEnumMeta", /* tp_name */
146-
sizeof(PyCspEnumMeta), /* tp_basicsize */
147-
0, /* tp_itemsize */
148-
(destructor) PyCspEnumMeta_dealloc, /* tp_dealloc */
149-
0, /* tp_print */
150-
0, /* tp_getattr */
151-
0, /* tp_setattr */
152-
0, /* tp_reserved */
153-
0, /* tp_repr */
154-
0, /* tp_as_number */
155-
0, /* tp_as_sequence */
156-
&PyCspEnumMeta_MappingMethods, /* tp_as_mapping */
157-
0, /* tp_hash */
158-
0, /* tp_call */
159-
0, /* tp_str */
160-
0, /* tp_getattro */
161-
0, /* tp_setattro */
162-
0, /* tp_as_buffer */
163-
Py_TPFLAGS_DEFAULT |
164-
Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TYPE_SUBCLASS, /* tp_flags */
165-
"csp enum metaclass", /* tp_doc */
166-
0, /* tp_traverse */
167-
0, /* tp_clear */
168-
0, /* tp_richcompare */
169-
0, /* tp_weaklistoffset */
170-
0, /* tp_iter */
171-
0, /* tp_iternext */
172-
0, /* tp_methods */
173-
0, /* tp_members */
174-
0, /* tp_getset */
175-
&PyType_Type, /* tp_base */
176-
0, /* tp_dict */
177-
0, /* tp_descr_get */
178-
0, /* tp_descr_set */
179-
0, /* tp_dictoffset */
180-
0, /*tp_init*/
181-
0, /* tp_alloc */
182-
(newfunc) PyCspEnumMeta_new,/* tp_new */
183-
PyObject_GC_Del, /* tp_free */
184-
};
185-
186-
187-
//PyCspEnum
188-
void PyCspEnum_dealloc( PyCspEnum * self )
189-
{
190-
self -> ~PyCspEnum();
191-
PyCspEnum::PyType.tp_free( self );
192-
}
193-
194-
PyObject * PyCspEnum_new( PyTypeObject * type, PyObject *args, PyObject *kwds )
195-
{
196-
CSP_BEGIN_METHOD;
197-
198-
PyObject * pyvalue;
199-
if( !PyArg_ParseTuple( args, "O", &pyvalue ) )
200-
CSP_THROW( PythonPassthrough, "" );
201-
202-
auto pymeta = (PyCspEnumMeta * ) type;
203-
PyObject * obj = nullptr;
204-
if( PyLong_Check( pyvalue ) )
205-
obj = PyDict_GetItem( pymeta -> enumsByValue.get(), pyvalue );
206-
else if( PyUnicode_Check( pyvalue ) )
207-
obj = PyDict_GetItem( pymeta -> enumsByName.get(), pyvalue );
208-
209-
if( !obj )
210-
CSP_THROW( ValueError, PyObjectPtr::incref( pyvalue ) << " is not a valid value on csp.enum type " << type -> tp_name );
211-
212-
Py_INCREF( obj );
213-
return obj;
214-
CSP_RETURN_NULL;
21533
}
21634

217-
PyObject * PyCspEnum_name( PyCspEnum * self, void * )
218-
{
219-
Py_INCREF( self -> enumName.get() );
220-
return self -> enumName.get();
221-
}
222-
223-
PyObject * PyCspEnum_value( PyCspEnum * self, void * )
224-
{
225-
Py_INCREF( self -> enumValue.get() );
226-
return self -> enumValue.get();
227-
}
228-
229-
static PyGetSetDef PyCspEnum_getset[] = {
230-
{ ( char * ) "name", (getter) PyCspEnum_name, 0, ( char * ) "string name of the enum instance", 0 },
231-
{ ( char * ) "value", (getter) PyCspEnum_value, 0, ( char * ) "long value of the enum instance", 0 },
232-
{ NULL }
233-
};
234-
235-
PyTypeObject PyCspEnum::PyType = {
236-
PyVarObject_HEAD_INIT(nullptr, 0)
237-
"_cspimpl.PyCspEnum", /* tp_name */
238-
sizeof(PyCspEnum), /* tp_basicsize */
239-
0, /* tp_itemsize */
240-
(destructor) PyCspEnum_dealloc, /* tp_dealloc */
241-
0, /* tp_print */
242-
0, /* tp_getattr */
243-
0, /* tp_setattr */
244-
0, /* tp_reserved */
245-
0, /* tp_repr */
246-
0, /* tp_as_number */
247-
0, /* tp_as_sequence */
248-
0, /* tp_as_mapping */
249-
0, /* tp_hash */
250-
0, /* tp_call */
251-
0, /* tp_str */
252-
0, /* tp_getattro */
253-
0, /* tp_setattro */
254-
0, /* tp_as_buffer */
255-
Py_TPFLAGS_DEFAULT |
256-
Py_TPFLAGS_BASETYPE, /* tp_flags */
257-
"csp enum", /* tp_doc */
258-
0, /* tp_traverse */
259-
0, /* tp_clear */
260-
0, /* tp_richcompare */
261-
0, /* tp_weaklistoffset */
262-
0, /* tp_iter */
263-
0, /* tp_iternext */
264-
0, /* tp_methods */
265-
0, /* tp_members */
266-
PyCspEnum_getset, /* tp_getset */
267-
0, /* tp_base */
268-
0, /* tp_dict */
269-
0, /* tp_descr_get */
270-
0, /* tp_descr_set */
271-
0, /* tp_dictoffset */
272-
0, /* tp_init */
273-
0, /* tp_alloc */
274-
(newfunc) PyCspEnum_new, /* tp_new */
275-
0, /* tp_free */
276-
};
277-
278-
REGISTER_TYPE_INIT( &PyCspEnumMeta::PyType, "PyCspEnumMeta" )
279-
REGISTER_TYPE_INIT( &PyCspEnum::PyType, "PyCspEnum" )
280-
28135
}

0 commit comments

Comments
 (0)