(site, portal_type_name)
return is_partially_generated, (tuple(base_class_list),
                                  portal_type_category_list,
                                  interface_class_list,
                                  attribute_dict)
Given a portal type, look up in Types Tool the corresponding
  Base Type object holding the definition of this portal type,
  and computes __bases__ and __dict__ for the class that will
  be created to represent this portal type

  Returns tuple with 4 items:
    - base_tuple: a tuple of classes to be used as __bases__
    - base_category_list: categories defined on the portal type
        (and portal type only: this excludes property sheets)
    - interface_list: list of zope interfaces the portal type implements
    - attribute dictionary: any additional attributes to put on the class
Given a portal type, look up in Types Tool the corresponding
  Base Type object holding the definition of this portal type,
  and computes __bases__ and __dict__ for the class that will
  be created to represent this portal type
Given a portal type look up in Types Tool the corresponding Base Type object holding the definition of this portal type and computes __bases__ and __dict__ for the class that will be created to represent this portal type
def generatePortalTypeClass(site, portal_type_name):
  """
  Given a portal type, look up in Types Tool the corresponding
  Base Type object holding the definition of this portal type,
  and computes __bases__ and __dict__ for the class that will
  be created to represent this portal type

  Returns tuple with 4 items:
    - base_tuple: a tuple of classes to be used as __bases__
    - base_category_list: categories defined on the portal type
        (and portal type only: this excludes property sheets)
    - interface_list: list of zope interfaces the portal type implements
    - attribute dictionary: any additional attributes to put on the class
  """
  # LOG("ERP5Type.dynamic", INFO, "Loading portal type " + portal_type_name)

  global core_portal_type_class_dict

  portal_type_category_list = []
  attribute_dict = dict(portal_type=portal_type_name,
                        _categories=[],
                        constraints=[])

  # When only the inner portal type class is loaded (ERP5Site initial
  # installation, recursive loads, bootstrap)
  is_partially_generated = False

  if portal_type_name in core_portal_type_class_dict:
    if not core_portal_type_class_dict[portal_type_name]['generating']:
      # Loading the (full) outer portal type class
      core_portal_type_class_dict[portal_type_name]['generating'] = True
    else:
      # Loading the inner portal type class without any mixin,
      # interface or Property Sheet
      klass = _importFilesystemClass(document_class_registry.get(
        core_portal_type_class_dict[portal_type_name]['type_class']))

      # LOG("ERP5Type.dynamic", INFO,
      #     "Loaded portal type %s (INNER)" % portal_type_name)

      # Don't do anything else, just allow to load fully the outer
      # portal type class
      is_partially_generated = True
      return is_partially_generated, ((klass,), [], [], attribute_dict)

  # Ugly but done only once on Workflows (not data) so no migration code:
  # Configurator Workflow implementation (workflow_module) used to have a
  # 'Variable' Portal Type. Because the name was too generic this has been
  # renamed to 'Workflow Variable' in ERP5 Workflow (portal_workflow)
  from .. import WITH_LEGACY_WORKFLOW
  if WITH_LEGACY_WORKFLOW and portal_type_name == 'Variable':
    portal_type_name = 'Workflow Variable'

  # Do not use __getitem__ (or _getOb) because portal_type may exist in a
  # type provider other than Types Tool.
  portal_type = getattr(site.portal_types, portal_type_name, None)

  type_class = None

  if portal_type is not None:
    # type_class has a compatibility getter that should return
    # something even if the field is not set (i.e. Base Type object
    # was not migrated yet). It only works if factory_method_id is set.
    type_class = portal_type.getTypeClass()

    # The Tools used to have 'Folder' or None as type_class instead of
    # 'NAME Tool', so make sure the type_class is correct
    #
    # NOTE: under discussion so might be removed later on
    if portal_type_name.endswith('Tool') and type_class in ('Folder', None):
      type_class = portal_type_name.replace(' ', '')

    mixin_list = portal_type.getTypeMixinList()
    interface_list = portal_type.getTypeInterfaceList()
    portal_type_category_list = portal_type.getTypeBaseCategoryList()
    attribute_dict['_categories'] = portal_type_category_list[:]
    acquire_local_role = bool(portal_type.getTypeAcquireLocalRole())
  else:
    LOG("ERP5Type.dynamic", WARNING,
        "Cannot find a portal type definition for '%s', trying to guess..."
        % portal_type_name)

  # But if neither factory_init_method_id nor type_class are set on
  # the portal type, we have to try to guess, for compatibility.
  # Moreover, some tools, such as 'Activity Tool', don't have any
  # portal type
  if type_class is None:
    if portal_type_name in core_portal_type_class_dict:
      # Only happen when portal_types is empty (e.g. when creating a
      # new ERP5Site)
      type_class = core_portal_type_class_dict[portal_type_name]['type_class']
      is_partially_generated = True
    else:
      # Missing Portal Type. This should not happen (all ERP5 object *must*
      # have a Portal Type and newTempXXX being deprecated) but try anyway to
      # figure out a coresponding Document class.
      type_class = portal_type_name.replace(' ', '')

    mixin_list = []
    interface_list = []
    acquire_local_role = True

  # Ugly but done only once on Workflows (not data) so no migration code:
  # Configurator Workflow implementation (workflow_module) used to have a
  # dedicated Portal Type for Transition Variable but this is now the same as
  # any other Workflow Variable.
  if WITH_LEGACY_WORKFLOW:
    if portal_type_name == 'Transition Variable':
      type_class = 'WorkflowVariable'
    elif portal_type_name in ('State', 'Transition'):
      type_class = 'Workflow' + portal_type_name

  if type_class is None:
    raise AttributeError('Document class is not defined on Portal Type ' + \
                           portal_type_name)

  klass = None
  if '.' in type_class:
    type_class_path = type_class
  else:
    type_class_path = None

    # Skip any document within ERP5Type Product as it is needed for
    # bootstrapping anyway
    type_class_namespace = document_class_registry.get(type_class, '')
    if not (type_class_namespace.startswith('Products.ERP5Type') or
            portal_type_name in core_portal_type_class_dict):
      if portal_type_name.endswith('Tool'):
        import erp5.component.tool
        klass = _importComponentClass(erp5.component.tool, type_class)

      # Tool Component was introduced recently and some Tool have already been
      # migrated as Document Component
      if klass is None:
        import erp5.component.document
        klass = _importComponentClass(erp5.component.document, type_class)

    if klass is None:
      type_class_path = document_class_registry.get(type_class)
      if type_class_path is None:
        raise AttributeError('Document class %s has not been registered:'
                             ' cannot import it as base of Portal Type %s'
                             % (type_class, portal_type_name))

  if klass is None:
    try:
      klass = _importFilesystemClass(type_class_path)
    except ImportError:
      error_msg = 'Could not import %s of Portal Type %s' % (type_class,
                                                             portal_type_name)

      LOG("ERP5Type.Dynamic", WARNING, error_msg, error=True)
      raise AttributeError(error_msg)

  global property_sheet_generating_portal_type_set

  accessor_holder_list = []

  if portal_type_name not in property_sheet_generating_portal_type_set:
    # LOG("ERP5Type.dynamic", INFO,
    #     "Filling accessor holder list for portal_type " + portal_type_name)

    property_sheet_generating_portal_type_set.add(portal_type_name)
    try:
      # Initialize ZODB Property Sheets accessor holders
      accessor_holder_list = createAllAccessorHolderList(site,
                                                         portal_type_name,
                                                         portal_type,
                                                         klass)

      base_category_set = set(attribute_dict['_categories'])
      for accessor_holder in accessor_holder_list:
        base_category_set.update(accessor_holder._categories)
        attribute_dict['constraints'].extend(accessor_holder.constraints)

      attribute_dict['_categories'] = list(base_category_set)
    finally:
      property_sheet_generating_portal_type_set.remove(portal_type_name)
  else:
    is_partially_generated = True

  # LOG("ERP5Type.dynamic", INFO,
  #     "Filled accessor holder list for portal_type %s (%s)" % \
  #     (portal_type_name, accessor_holder_list))

  mixin_class_list = []
  if mixin_list:
    # Only one Mixin class per ZODB Component (!= FS) where module_name ==
    # class_name, name ending with 'Mixin'.
    #
    # Rationale: same as Document/Interface; consistent naming; avoid a
    # registry like there used to be with FS.
    import erp5.component.mixin
    for mixin in mixin_list:
      mixin_class = _importComponentClass(erp5.component.mixin, mixin)
      if mixin_class is None:
        mixin_class = _importFilesystemClass(mixin_class_registry[mixin])

      mixin_class_list.append(mixin_class)

  base_class_list = [klass] + accessor_holder_list + mixin_class_list + [
    # _getAcquireLocalRoles is accessed by security machinery, so it needs to
    # be fast: make it a ConstantGetter while we have access to portal_type
    # configuration.
    ACQUIRE_LOCAL_ROLE_GETTER_DICT[acquire_local_role],
  ]

  interface_class_list = []
  if interface_list:
    # Filesystem Interfaces may have defined several Interfaces in one file
    # but only *one* Interface per ZODB Component where module_name ==
    # class_name, name starting with 'I'.
    #
    # Rationale: same as Document/Mixin; consistent naming; avoid a registry
    # like there used to be for Mixin or importing all class in
    # Products.ERP5Type.interfaces.__init__.py.
    import erp5.component.interface
    from Products.ERP5Type import interfaces as filesystem_interfaces
    for interface in interface_list:
      interface_class = _importComponentClass(erp5.component.interface, interface)
      if interface_class is None:
        interface_class = getattr(filesystem_interfaces, interface)

      interface_class_list.append(interface_class)

  if portal_type_name in core_portal_type_class_dict:
    core_portal_type_class_dict[portal_type_name]['generating'] = False

  attribute_dict['_restricted_setter_set'] = {method
        for ancestor in base_class_list
        for permissions in getattr(ancestor, '__ac_permissions__', ())
        if permissions[0] not in ('Access contents information',
                                  'Modify portal content')
        for method in permissions[1]
        if method.startswith('set')}

  attribute_dict['_restricted_getter_set'] = {method
        for ancestor in base_class_list
        for permissions in getattr(ancestor, '__ac_permissions__', ())
        if permissions[0] not in ('Access contents information', )
        for method in permissions[1]
        if method.startswith('get')}

  #LOG("ERP5Type.dynamic", INFO,
  #    "Portal type %s loaded with bases %s" \
  #        % (portal_type_name, repr(base_class_list)))

  return is_partially_generated, (tuple(base_class_list),
                                  portal_type_category_list,
                                  interface_class_list,
                                  attribute_dict)
def generatePortalTypeClass ( site , portal_type_name ) : # LOG("ERP5Type.dynamic", INFO, "Loading portal type " + portal_type_name) global core_portal_type_class_dict portal_type_category_list = [ ] attribute_dict = dict ( portal_type = portal_type_name , _categories = [ ] , constraints = [ ] ) # When only the inner portal type class is loaded (ERP5Site initial # installation, recursive loads, bootstrap) is_partially_generated = False if portal_type_name in core_portal_type_class_dict : if not core_portal_type_class_dict [ portal_type_name ] [ 'generating' ] : # Loading the (full) outer portal type class core_portal_type_class_dict [ portal_type_name ] [ 'generating' ] = True else : # Loading the inner portal type class without any mixin, # interface or Property Sheet klass = _importFilesystemClass ( document_class_registry . get ( core_portal_type_class_dict [ portal_type_name ] [ 'type_class' ] ) ) # LOG("ERP5Type.dynamic", INFO, #     "Loaded portal type %s (INNER)" % portal_type_name) # Don't do anything else, just allow to load fully the outer # portal type class is_partially_generated = True return is_partially_generated , ( ( klass , ) , [ ] , [ ] , attribute_dict ) # Ugly but done only once on Workflows (not data) so no migration code: # Configurator Workflow implementation (workflow_module) used to have a # 'Variable' Portal Type. Because the name was too generic this has been # renamed to 'Workflow Variable' in ERP5 Workflow (portal_workflow) from . . import WITH_LEGACY_WORKFLOW if WITH_LEGACY_WORKFLOW and portal_type_name == 'Variable' : portal_type_name = 'Workflow Variable' # Do not use __getitem__ (or _getOb) because portal_type may exist in a # type provider other than Types Tool. portal_type = getattr ( site . portal_types , portal_type_name , None ) type_class = None if portal_type is not None : # type_class has a compatibility getter that should return # something even if the field is not set (i.e. Base Type object # was not migrated yet). It only works if factory_method_id is set. type_class = portal_type . getTypeClass ( ) # The Tools used to have 'Folder' or None as type_class instead of # 'NAME Tool', so make sure the type_class is correct # # NOTE: under discussion so might be removed later on if portal_type_name . endswith ( 'Tool' ) and type_class in ( 'Folder' , None ) : type_class = portal_type_name . replace ( ' ' , '' ) mixin_list = portal_type . getTypeMixinList ( ) interface_list = portal_type . getTypeInterfaceList ( ) portal_type_category_list = portal_type . getTypeBaseCategoryList ( ) attribute_dict [ '_categories' ] = portal_type_category_list [ : ] acquire_local_role = bool ( portal_type . getTypeAcquireLocalRole ( ) ) else : LOG ( "ERP5Type.dynamic" , WARNING , "Cannot find a portal type definition for '%s', trying to guess..." % portal_type_name ) # But if neither factory_init_method_id nor type_class are set on # the portal type, we have to try to guess, for compatibility. # Moreover, some tools, such as 'Activity Tool', don't have any # portal type if type_class is None : if portal_type_name in core_portal_type_class_dict : # Only happen when portal_types is empty (e.g. when creating a # new ERP5Site) type_class = core_portal_type_class_dict [ portal_type_name ] [ 'type_class' ] is_partially_generated = True else : # Missing Portal Type. This should not happen (all ERP5 object *must* # have a Portal Type and newTempXXX being deprecated) but try anyway to # figure out a coresponding Document class. type_class = portal_type_name . replace ( ' ' , '' ) mixin_list = [ ] interface_list = [ ] acquire_local_role = True # Ugly but done only once on Workflows (not data) so no migration code: # Configurator Workflow implementation (workflow_module) used to have a # dedicated Portal Type for Transition Variable but this is now the same as # any other Workflow Variable. if WITH_LEGACY_WORKFLOW : if portal_type_name == 'Transition Variable' : type_class = 'WorkflowVariable' elif portal_type_name in ( 'State' , 'Transition' ) : type_class = 'Workflow' + portal_type_name if type_class is None : raise AttributeError ( 'Document class is not defined on Portal Type ' + portal_type_name ) klass = None if '.' in type_class : type_class_path = type_class else : type_class_path = None # Skip any document within ERP5Type Product as it is needed for # bootstrapping anyway type_class_namespace = document_class_registry . get ( type_class , '' ) if not ( type_class_namespace . startswith ( 'Products.ERP5Type' ) or portal_type_name in core_portal_type_class_dict ) : if portal_type_name . endswith ( 'Tool' ) : import erp5 . component . tool klass = _importComponentClass ( erp5 . component . tool , type_class ) # Tool Component was introduced recently and some Tool have already been # migrated as Document Component if klass is None : import erp5 . component . document klass = _importComponentClass ( erp5 . component . document , type_class ) if klass is None : type_class_path = document_class_registry . get ( type_class ) if type_class_path is None : raise AttributeError ( 'Document class %s has not been registered:' ' cannot import it as base of Portal Type %s' % ( type_class , portal_type_name ) ) if klass is None : try : klass = _importFilesystemClass ( type_class_path ) except ImportError : error_msg = 'Could not import %s of Portal Type %s' % ( type_class , portal_type_name ) LOG ( "ERP5Type.Dynamic" , WARNING , error_msg , error = True ) raise AttributeError ( error_msg ) global property_sheet_generating_portal_type_set accessor_holder_list = [ ] if portal_type_name not in property_sheet_generating_portal_type_set : # LOG("ERP5Type.dynamic", INFO, #     "Filling accessor holder list for portal_type " + portal_type_name) property_sheet_generating_portal_type_set . add ( portal_type_name ) try : # Initialize ZODB Property Sheets accessor holders accessor_holder_list = createAllAccessorHolderList ( site , portal_type_name , portal_type , klass ) base_category_set = set ( attribute_dict [ '_categories' ] ) for accessor_holder in accessor_holder_list : base_category_set . update ( accessor_holder . _categories ) attribute_dict [ 'constraints' ] . extend ( accessor_holder . constraints ) attribute_dict [ '_categories' ] = list ( base_category_set ) finally : property_sheet_generating_portal_type_set . remove ( portal_type_name ) else : is_partially_generated = True # LOG("ERP5Type.dynamic", INFO, #     "Filled accessor holder list for portal_type %s (%s)" % \ #     (portal_type_name, accessor_holder_list)) mixin_class_list = [ ] if mixin_list : # Only one Mixin class per ZODB Component (!= FS) where module_name == # class_name, name ending with 'Mixin'. # # Rationale: same as Document/Interface; consistent naming; avoid a # registry like there used to be with FS. import erp5 . component . mixin for mixin in mixin_list : mixin_class = _importComponentClass ( erp5 . component . mixin , mixin ) if mixin_class is None : mixin_class = _importFilesystemClass ( mixin_class_registry [ mixin ] ) mixin_class_list . append ( mixin_class ) base_class_list = [ klass ] + accessor_holder_list + mixin_class_list + [ # _getAcquireLocalRoles is accessed by security machinery, so it needs to # be fast: make it a ConstantGetter while we have access to portal_type # configuration. ACQUIRE_LOCAL_ROLE_GETTER_DICT [ acquire_local_role ] , ] interface_class_list = [ ] if interface_list : # Filesystem Interfaces may have defined several Interfaces in one file # but only *one* Interface per ZODB Component where module_name == # class_name, name starting with 'I'. # # Rationale: same as Document/Mixin; consistent naming; avoid a registry # like there used to be for Mixin or importing all class in # Products.ERP5Type.interfaces.__init__.py. import erp5 . component . interface from Products . ERP5Type import interfaces as filesystem_interfaces for interface in interface_list : interface_class = _importComponentClass ( erp5 . component . interface , interface ) if interface_class is None : interface_class = getattr ( filesystem_interfaces , interface ) interface_class_list . append ( interface_class ) if portal_type_name in core_portal_type_class_dict : core_portal_type_class_dict [ portal_type_name ] [ 'generating' ] = False attribute_dict [ '_restricted_setter_set' ] = { method for ancestor in base_class_list for permissions in getattr ( ancestor , '__ac_permissions__' , ( ) ) if permissions [ 0 ] not in ( 'Access contents information' , 'Modify portal content' ) for method in permissions [ 1 ] if method . startswith ( 'set' ) } attribute_dict [ '_restricted_getter_set' ] = { method for ancestor in base_class_list for permissions in getattr ( ancestor , '__ac_permissions__' , ( ) ) if permissions [ 0 ] not in ( 'Access contents information' , ) for method in permissions [ 1 ] if method . startswith ( 'get' ) } #LOG("ERP5Type.dynamic", INFO, #    "Portal type %s loaded with bases %s" \ #        % (portal_type_name, repr(base_class_list))) return is_partially_generated , ( tuple ( base_class_list ) , portal_type_category_list , interface_class_list , attribute_dict )