Skip to content

Commit

Permalink
Learn how to do enumerated constants as class bodies in cython
Browse files Browse the repository at this point in the history
Define them as an instance, make an accessor function, type the result
of the accessor in the pxd. This avoids needing to get the type in the
python file, which is what causes the leading underscore problems.

This avoids the need for re-enumerating all the constants while still
being direct access (yay!). Speeds things up by another percent or two
in datastructures.py.
  • Loading branch information
jamadden committed Jun 27, 2018
1 parent b20aed4 commit bed7e65
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 97 deletions.
4 changes: 2 additions & 2 deletions setup.py
Expand Up @@ -69,14 +69,14 @@ def cythonize(extensions, **_kwargs):
'nti.externalization._' + mod_name,
sources=["src/nti/externalization/" + mod_name + '.py'],
depends=["src/nti/externalization/_" + mod_name + '.pxd'],
define_macros=[('CYTHON_TRACE', '1')],
#define_macros=[('CYTHON_TRACE', '1')],
))

ext_modules = cythonize(
ext_modules,
annotate=True,
compiler_directives={
'linetrace': True,
#'linetrace': True,
'infer_types': True,
},
)
Expand Down
37 changes: 37 additions & 0 deletions src/nti/externalization/__base_interfaces.pxd
Expand Up @@ -15,3 +15,40 @@ cdef class LocatedExternalDict(dict):
cdef readonly mimeType

cpdef LocatedExternalDict make_external_dict()

cdef class StandardExternalFields(object):

cdef readonly unicode ID
cdef readonly unicode OID
cdef readonly unicode HREF
cdef readonly unicode INTID
cdef readonly unicode NTIID
cdef readonly unicode CREATOR
cdef readonly unicode CONTAINER_ID
cdef readonly unicode CREATED_TIME
cdef readonly unicode LAST_MODIFIED
cdef readonly unicode CLASS
cdef readonly unicode LINKS
cdef readonly unicode MIMETYPE
cdef readonly unicode ITEMS
cdef readonly unicode TOTAL
cdef readonly unicode ITEM_COUNT
cdef readonly frozenset ALL

cdef StandardExternalFields _standard_external_fields

cpdef StandardExternalFields get_standard_external_fields()

cdef class StandardInternalFields(object):

cdef readonly str ID
cdef readonly str NTIID
cdef readonly str CREATOR
cdef readonly str CREATED_TIME
cdef readonly str CONTAINER_ID
cdef readonly str LAST_MODIFIED
cdef readonly str LAST_MODIFIEDU

cdef StandardInternalFields _standard_internal_fields

cpdef StandardInternalFields get_standard_internal_fields()
87 changes: 87 additions & 0 deletions src/nti/externalization/_base_interfaces.py
Expand Up @@ -68,5 +68,92 @@ def make_external_dict():
# a type, and then it correctly infers that type for the variable.
return LocatedExternalDict()


class StandardExternalFields(object):
"""
Namespace object defining constants whose values are the
keys used in external mappings.
These are text (unicode).
"""
__slots__ = (
'ID',
'OID',
'HREF',
'INTID',
'NTIID',
'CREATOR',
'CONTAINER_ID',
'CREATED_TIME',
'LAST_MODIFIED',
'CLASS',
'LINKS',
'MIMETYPE',
'ITEMS',
'TOTAL',
'ITEM_COUNT',
'ALL'
)

def __init__(self):
self.ID = u'ID'
self.OID = u'OID'
self.HREF = u'href'
self.INTID = u'INTID'
self.NTIID = u'NTIID'
self.CREATOR = u'Creator'
self.CONTAINER_ID = u'ContainerId'
self.CREATED_TIME = u'CreatedTime'
self.LAST_MODIFIED = u'Last Modified'
self.CLASS = u'Class'
self.LINKS = u'Links'
self.MIMETYPE = u'MimeType'
self.ITEMS = u'Items'
self.TOTAL = u'Total'
self.ITEM_COUNT = u'ItemCount'
self.ALL = frozenset(StandardExternalFields.__slots__) - {'ALL'}


_standard_external_fields = StandardExternalFields()

def get_standard_external_fields():
return _standard_external_fields



class StandardInternalFields(object):
"""
Namespace object defining constants whose values are the
property/attribute names looked for on internal objects.
These must be native strings.
"""

__slots__ = (
'ID',
'NTIID',
'CREATOR',
'CREATED_TIME',
'CONTAINER_ID',
'LAST_MODIFIED',
'LAST_MODIFIEDU',
)

def __init__(self):
self.ID = 'id'
self.NTIID = 'ntiid'
self.CREATOR = 'creator'
self.CREATED_TIME = 'createdTime'
self.CONTAINER_ID = 'containerId'
self.LAST_MODIFIED = 'lastModified'
self.LAST_MODIFIEDU = 'LastModified'


_standard_internal_fields = StandardInternalFields()

def get_standard_internal_fields():
return _standard_internal_fields


from nti.externalization._compat import import_c_accel # pylint:disable=wrong-import-position
import_c_accel(globals(), 'nti.externalization.__base_interfaces')
9 changes: 6 additions & 3 deletions src/nti/externalization/_datastructures.pxd
Expand Up @@ -8,11 +8,14 @@ from nti.externalization._externalization cimport _isMagicKey
from nti.externalization._externalization cimport to_minimal_standard_external_dictionary
from nti.externalization._externalization cimport to_standard_external_dictionary


from nti.externalization.__base_interfaces cimport get_standard_external_fields
from nti.externalization.__base_interfaces cimport StandardExternalFields as SEF
from nti.externalization.__base_interfaces cimport get_standard_internal_fields
from nti.externalization.__base_interfaces cimport StandardInternalFields as SIF

cdef IInternalObjectIO
cdef StandardExternalFields
cdef StandardInternalFields
cdef SEF StandardExternalFields
cdef SIF StandardInternalFields
cdef validate_named_field_value
cdef make_repr
cdef isSyntheticKey
Expand Down
21 changes: 9 additions & 12 deletions src/nti/externalization/_externalization.pxd
Expand Up @@ -3,6 +3,15 @@ import cython

from .__base_interfaces cimport make_external_dict

from nti.externalization.__base_interfaces cimport get_standard_external_fields
from nti.externalization.__base_interfaces cimport StandardExternalFields as SEF
from nti.externalization.__base_interfaces cimport get_standard_internal_fields
from nti.externalization.__base_interfaces cimport StandardInternalFields as SIF


cdef SEF StandardExternalFields
cdef SIF StandardInternalFields


# Imports
cdef numbers
Expand Down Expand Up @@ -33,18 +42,6 @@ cdef NotGiven


# Constants
cdef unicode StandardExternalFields_CLASS
cdef unicode StandardExternalFields_CREATOR
cdef unicode StandardExternalFields_MIMETYPE
cdef unicode StandardExternalFields_CONTAINER_ID
cdef unicode StandardExternalFields_CREATED_TIME
cdef unicode StandardExternalFields_LAST_MODIFIED

cdef str StandardInternalFields_CREATOR
cdef str StandardInternalFields_CONTAINER_ID
cdef str StandardInternalFields_CREATED_TIME
cdef str StandardInternalFields_LAST_MODIFIED
cdef str StandardInternalFields_LAST_MODIFIEDU


cdef _manager, _manager_get, _manager_pop, _manager_push
Expand Down
11 changes: 10 additions & 1 deletion src/nti/externalization/datastructures.py
Expand Up @@ -8,6 +8,11 @@
from __future__ import division
from __future__ import print_function

# There are a *lot* of fixme (XXX and the like) in this file.
# Turn those off in general so we can see through the noise.
# pylint:disable=fixme


# stdlib imports
import numbers
from weakref import WeakSet
Expand All @@ -26,11 +31,15 @@
# that breaks cython
from .externalization import toExternalObject as _toExternalObject
from .interfaces import IInternalObjectIO
from .interfaces import StandardExternalFields
from .interfaces import StandardInternalFields
from .internalization import validate_named_field_value
from .representation import make_repr

from ._base_interfaces import get_standard_external_fields
from ._base_interfaces import get_standard_internal_fields

StandardExternalFields = get_standard_external_fields()
StandardInternalFields = get_standard_internal_fields()

class ExternalizableDictionaryMixin(object):
"""
Expand Down
61 changes: 27 additions & 34 deletions src/nti/externalization/externalization.py
Expand Up @@ -52,8 +52,14 @@
from .interfaces import ILocatedExternalSequence
from .interfaces import INonExternalizableReplacement
from .interfaces import INonExternalizableReplacer
from .interfaces import StandardExternalFields
from .interfaces import StandardInternalFields


from ._base_interfaces import get_standard_external_fields
from ._base_interfaces import get_standard_internal_fields

StandardExternalFields = get_standard_external_fields()
StandardInternalFields = get_standard_internal_fields()




Expand All @@ -68,19 +74,6 @@
# which are much faster. Check the annotated .c file for details.
###

# Local for speed (remember these are declared in .pxd)
StandardExternalFields_CLASS = StandardExternalFields.CLASS
StandardExternalFields_CREATOR = StandardExternalFields.CREATOR
StandardExternalFields_MIMETYPE = StandardExternalFields.MIMETYPE
StandardExternalFields_CONTAINER_ID = StandardExternalFields.CONTAINER_ID
StandardExternalFields_CREATED_TIME = StandardExternalFields.CREATED_TIME
StandardExternalFields_LAST_MODIFIED = StandardExternalFields.LAST_MODIFIED

StandardInternalFields_CREATOR = StandardInternalFields.CREATOR
StandardInternalFields_CONTAINER_ID = StandardInternalFields.CONTAINER_ID
StandardInternalFields_CREATED_TIME = StandardInternalFields.CREATED_TIME
StandardInternalFields_LAST_MODIFIED = StandardInternalFields.LAST_MODIFIED
StandardInternalFields_LAST_MODIFIEDU = StandardInternalFields.LAST_MODIFIEDU

SYSTEM_USER_NAME = getattr(system_user, 'title').lower()

Expand Down Expand Up @@ -450,7 +443,7 @@ def choose_field(result, self, ext_name,
if value is not None:
# If the creator is the system user, catch it here
# XXX: Document this behaviour.
if ext_name == StandardExternalFields_CREATOR:
if ext_name == StandardExternalFields.CREATOR:
if is_system_user(value):
value = SYSTEM_USER_NAME
else:
Expand Down Expand Up @@ -490,12 +483,12 @@ def to_standard_external_last_modified_time(context, default=None, _write_into=N
# to_standard_external_dictionary
holder = _write_into if _write_into is not None else dict()

choose_field(holder, context, StandardExternalFields_LAST_MODIFIED,
fields=(StandardInternalFields_LAST_MODIFIED,
StandardInternalFields_LAST_MODIFIEDU),
choose_field(holder, context, StandardExternalFields.LAST_MODIFIED,
fields=(StandardInternalFields.LAST_MODIFIED,
StandardInternalFields.LAST_MODIFIEDU),
sup_iface=IDCTimes, sup_fields=('modified',),
sup_converter=_datetime_to_epoch)
return holder.get(StandardExternalFields_LAST_MODIFIED, default)
return holder.get(StandardExternalFields.LAST_MODIFIED, default)


def to_standard_external_created_time(context, default=None, _write_into=None):
Expand All @@ -512,12 +505,12 @@ def to_standard_external_created_time(context, default=None, _write_into=None):
# to_standard_external_dictionary
holder = _write_into if _write_into is not None else dict()

choose_field(holder, context, StandardExternalFields_CREATED_TIME,
fields=(StandardInternalFields_CREATED_TIME,),
choose_field(holder, context, StandardExternalFields.CREATED_TIME,
fields=(StandardInternalFields.CREATED_TIME,),
sup_iface=IDCTimes, sup_fields=('created',),
sup_converter=_datetime_to_epoch)

return holder.get(StandardExternalFields_CREATED_TIME, default)
return holder.get(StandardExternalFields.CREATED_TIME, default)


_ext_class_ignored_modules = frozenset(('nti.externalization',
Expand All @@ -528,36 +521,36 @@ def to_standard_external_created_time(context, default=None, _write_into=None):
'nti.externalization.__base_interfaces'))

def _ext_class_if_needed(self, result):
if StandardExternalFields_CLASS not in result:
if StandardExternalFields.CLASS not in result:
cls = getattr(self, '__external_class_name__', None)
if cls:
result[StandardExternalFields_CLASS] = cls
result[StandardExternalFields.CLASS] = cls
elif (not self.__class__.__name__.startswith('_')
and self.__class__.__module__ not in _ext_class_ignored_modules):
result[StandardExternalFields_CLASS] = self.__class__.__name__
result[StandardExternalFields.CLASS] = self.__class__.__name__



def _should_never_convert(x):
raise AssertionError("We should not be converting")

_CREATOR_FIELDS = (StandardInternalFields_CREATOR,
StandardExternalFields_CREATOR)
_CREATOR_FIELDS = (StandardInternalFields.CREATOR,
StandardExternalFields.CREATOR)

def _fill_creator(result, self):
choose_field(result, self, StandardExternalFields_CREATOR,
choose_field(result, self, StandardExternalFields.CREATOR,
_should_never_convert,
_CREATOR_FIELDS)

_CONTAINER_FIELDS = (StandardInternalFields_CONTAINER_ID,)
_CONTAINER_FIELDS = (StandardInternalFields.CONTAINER_ID,)

def _fill_container(result, self):
containerId = choose_field(result, self, StandardExternalFields_CONTAINER_ID,
containerId = choose_field(result, self, StandardExternalFields.CONTAINER_ID,
identity,
_CONTAINER_FIELDS)
if containerId is not None:
# alias per mobile client request 20150625
result[StandardInternalFields_CONTAINER_ID] = containerId
result[StandardInternalFields.CONTAINER_ID] = containerId


def to_standard_external_dictionary(
Expand Down Expand Up @@ -645,7 +638,7 @@ def to_minimal_standard_external_dictionary(self, mergeFrom=None):

mime_type = getattr(self, 'mimeType', None) or getattr(self, 'mime_type', None)
if mime_type is not None and mime_type:
result[StandardExternalFields_MIMETYPE] = mime_type
result[StandardExternalFields.MIMETYPE] = mime_type
return result


Expand Down Expand Up @@ -695,5 +688,5 @@ def _clean(m):
EXT_FORMAT_JSON = 'json'


from nti.externalization._compat import import_c_accel # pylint:disable=wrong-import-position
from nti.externalization._compat import import_c_accel # pylint:disable=wrong-import-position,wrong-import-order
import_c_accel(globals(), 'nti.externalization._externalization')

0 comments on commit bed7e65

Please sign in to comment.