def __getattr__(self, name): """Try to pick any attribute (not found on this change record--this method only gets called when a non-existent attribute is accessed) off the related audit snapshot record (as every change record is related to a type-dedicated audit record). !+ should this be on Change i.e. for all change actions? """ audit = self.audit try: return getattr(audit, name) except AttributeError: # !+DocVersion.filevers try: from bungeni.alchemist import utils return utils.FILES_VERSION_CONTAINER_ATTRIBUTE_ERROR_HACK( self, name) except: import sys from bungeni.utils import probing probing.log_exc(sys.exc_info(), log_handler=log.error) raise AttributeError( "%r [audit_id=%r, audit_type=%r] object has no attribute %r" % (self, self.audit_id, self.audit_type, name))
def get_head_object_state_rpm(sub_context): """IRolePermissionMap(context) adapter factory. Lighweight and high-performance wrapper on get_object_state(context), to *lookup* (note: no creation of any instance) the workflow State singleton instance for the sub context's head's status. Note that sub context is NOT workflowed. On lookup error, returns NONE_STATE_RPM, instead of what would be a zope.component.ComponentLookupError. """ try: head = sub_context.head return interfaces.IWorkflow(head).get_state(sub_context.status) except interfaces.InvalidStateError: from bungeni.models.interfaces import IChange if sub_context.status is None and IChange.providedBy(sub_context): # if status is None,then must have an "add" change action... ignore. assert sub_context.action == "add" else: log.error("get_head_object_state_rpm:%s:%s", type(sub_context).__name__, sub_context.pk) probing.log_exc(sys.exc_info(), log_handler=log.error) return NONE_STATE_RPM
def createAndAdd(self, data): domain_model = self.domain_model # create the object, inspect data for constructor args try: ob = createInstance(domain_model, data) except TypeError: log.error("Failure: createInstance(%s, %s)", domain_model, data) probing.log_exc(sys.exc_info(), log_handler=log.error) ob = domain_model() # apply any context values self.finishConstruction(ob) # apply extra form values formlib.form.applyChanges(ob, self.form_fields, data, self.adapters) # set the object in container context, causing autosetting of # constrained values e.g. one2many attributes, by triggering call to # _ManagedContainer.constraints.setConstrainedValues() self.context[""] = ob # flush so we have database id Session().flush() # !+DataError reload form and display this error? # fire an object created event notify(ObjectCreatedEvent(ob)) # !+ would set doc_id (if session not flushed) !! # signal to add form machinery to go to next url self._finished_add = True # retrieve the object with location and security information oid = self.get_oid(ob) return self.context[oid]
def get_object_state_rpm(context): """IRolePermissionMap(context) adapter factory. Looks up the workflow State singleton instance that is the current IRolePermissionMap responsible for the context. Lighweight and high-performance wrapper on get_object_state(context), to *lookup* (note: no creation of any instance) the workflow State singleton instance. On lookup error, returns NONE_STATE_RPM, instead of what would be a zope.component.ComponentLookupError. """ try: state = get_object_state(context) except interfaces.InvalidStateError: log.error("get_object_state_rpm:%s:%s", type(context).__name__, context.pk) probing.log_exc(sys.exc_info(), log_handler=log.error) return NONE_STATE_RPM if state.parent_permissions: # this state delegates permissions to parent, # so just recurse passing parent item instead head = context.head return get_object_state_rpm(head) return state
def page_description(self): """Formalize view.page_description as a view property to factor the logic for determining the page description for a view out of the template. Templates should always simply call: view.page_description """ # if view explicitly sets a page_description, use it if self._page_description: return self._page_description # otherwise try to determine it from DC annotations context = removeSecurityProxy(self.context) try: # This is equivalent of the ZPT expression "context/dc:description" # i.e. to "load the value of the variable context, then find a # component that adapts that object to Dublin Core and read the # description attribute of the component." return IDCDescriptiveProperties(context).description except (Exception, ): probing.log_exc(sys.exc_info(), log_handler=log.debug) # otherwise try to determine it from the context if getattr(context, "description", None): return context.description else: return "Bungeni"
def createAndAdd(self, data): domain_model = self.domain_model # create the object, inspect data for constructor args try: ob = createInstance(domain_model, data) except TypeError: log.error("Failure: createInstance(%s, %s)", domain_model, data) probing.log_exc(sys.exc_info(), log_handler=log.error) ob = domain_model() # apply any context values self.finishConstruction(ob) # apply extra form values formlib.form.applyChanges(ob, self.form_fields, data, self.adapters) # set the object in container context, causing autosetting of # constrained values e.g. one2many attributes, by triggering call to # _ManagedContainer.constraints.setConstrainedValues() self.context[""] = ob # flush so we have database id Session().flush() # !+DataError reload form and display this error? # fire an object created event notify(ObjectCreatedEvent( ob)) # !+ would set doc_id (if session not flushed) !! # signal to add form machinery to go to next url self._finished_add = True # retrieve the object with location and security information oid = self.get_oid(ob) return self.context[oid]
def __getattr__(self, name): """Try to pick any attribute (not found on this change record--this method only gets called when a non-existent attribute is accessed) off the related audit snapshot record (as every change record is related to a type-dedicated audit record). !+ should this be on Change i.e. for all change actions? """ audit = self.audit try: return getattr(audit, name) except AttributeError: # !+DocVersion.filevers try: from bungeni.alchemist import utils return utils.FILES_VERSION_CONTAINER_ATTRIBUTE_ERROR_HACK(self, name) except: import sys from bungeni.utils import probing probing.log_exc(sys.exc_info(), log_handler=log.error) raise AttributeError( "%r [audit_id=%r, audit_type=%r] object has no attribute %r" % (self, self.audit_id, self.audit_type, name) )
def filter_group(self, query, domain_class, kw): try: group_id = int(kw.get("filter_group", 0) or 0) # incoming value is # typically the "" empty string, resulting in exception noise below except (TypeError, ValueError): probing.log_exc(sys.exc_info(), log_handler=log.error) group_id = 0 if group_id: if hasattr(domain_class, "group_id"): query = query.filter(domain_class.group_id == group_id) elif hasattr(domain_class, "chamber_id"): query = query.filter(domain_class.chamber_id == group_id) return query
def filter_group(self, query, domain_class, kw): try: group_id = int(kw.get("filter_group", 0) or 0) # incoming value is # typically the "" empty string, resulting in exception noise below except (TypeError, ValueError): probing.log_exc(sys.exc_info(), log_handler=log.error) group_id = 0 if group_id: if hasattr(domain_class, "group_id"): query = query.filter(domain_class.group_id==group_id) elif hasattr(domain_class, "chamber_id"): query = query.filter(domain_class.chamber_id==group_id) return query
def descriptor_classes(): """A generator of descriptor classes in this module, preserving the order of definition. """ # dir() returns names in alphabetical order decorated = [] for key in dir(module): cls = getattr(module, key) try: assert IModelDescriptor.implementedBy(cls) # we decorate with the source code line number for the cls decorated.append((inspect.getsourcelines(cls)[1], cls)) except (TypeError, AttributeError, AssertionError): probing.log_exc(sys.exc_info(), log_handler=log.debug) # we yield each cls in order of definition for cls in [ cls for (line_num, cls) in sorted(decorated) ]: yield cls
def descriptor_classes(): """A generator of descriptor classes in this module, preserving the order of definition. """ # dir() returns names in alphabetical order decorated = [] for key in dir(module): cls = getattr(module, key) try: assert IModelDescriptor.implementedBy(cls) # we decorate with the source code line number for the cls decorated.append((inspect.getsourcelines(cls)[1], cls)) except (TypeError, AttributeError, AssertionError): probing.log_exc(sys.exc_info(), log_handler=log.debug) # we yield each cls in order of definition for cls in [cls for (line_num, cls) in sorted(decorated)]: yield cls
def absoluteURL(context, request): """ For cleaner public URLs, we ensure to use an empty string instead of "index". Throughout bungeni and ploned packages, this function should ALWAYS be used instead of zope.traversing.browser.absoluteURL. """ try: url = zope.traversing.browser.absoluteURL(context, request).split("/") except: # !+ABSOLUTE_URL: TypeError: There isn't enough context to get URL information. # This is probably due to incorrect setting up of location information. probing.log_exc(sys.exc_info(), log_handler=log.error) log.error("\n ...CONTEXT: %s\n ...REQUEST URL: %s", context, request.getURL()) return "" while url[-1] in indexNames: log.warn("POPPING: %s -> %s", "/".join(url), url[-1]) url.pop() return "/".join(url)
def add_zcml_menu_items(self, container): """Add the list of ZCML menu items (if any) for this top-level container. Top-level section given by container may define a menu in ZCML with naming convention: <container_name>_navigation. """ # !+ turn this into a utility zcml_menu_name_template = "%s_navigation" try: menu_name = zcml_menu_name_template % container.__name__ menu = component.getUtility(IBrowserMenu, name=menu_name) items = menu.getMenuItems(container, self.request) except (Exception,): probing.log_exc(sys.exc_info(), log_handler=log.debug) return [] # OK, do any necessary post-processing of each menu item local_url = url.absoluteURL(container, self.request) site_url = url.absoluteURL(getSite(), self.request) request_url = self.request.getURL() default_view_name = queryDefaultViewName(container, self.request) selection = None for item in sorted(items, key=lambda item: item["action"], reverse=True): action = item["action"] if default_view_name==action.lstrip("@@"): _url = local_url if selection is None: selected = sameProxiedObjects(container, self.context) else: _url = make_absolute(action, local_url, site_url) if selection is None: selected = pos_action_in_url(action, request_url) item["url"] = _url item["selected"] = selected and u"selected" or u"" if selected: # self is marker selection = self selected = False self.items.append(item)
def validate_derived_table_schema(self, action, data): """Look-ahead validate against database contraints. """ dm = self.domain_model ti = capi.get_type_info(dm) derived_table_schema = ti.derived_table_schema errors = [] for name in derived_table_schema: ff = self.form_fields.get(name) # skip if no form does not define a corresponding form field if not ff: continue # !+ skip if field not included in this form "mode"? #if name not in getattr("%s_columns" % (self.mode), ti.descriptor_model): # continue field = derived_table_schema.get(name) assert field is ff.field, name # !+TMP sanity check value = data.get(name, None) ''' !+VTF disabling for now, as attempting to preset a contextualized vocab fails for subsequent lodaing of a *view*, as this is executed when validating a *submit* -- may still work if executed right moment. # !+VTF bungeni.ui.fields.VocabularyTextField -- here a vocabulary # field is not necessarily the type defined by bungeni # e.g. zope.schema._field.Choice that during validation attempts to # retrieve the vocabulary and initialize it with a None context... # To preempt the error that this causes, we check for and stuff # an appropiately initialized vocabulary instance onto this field... if hasattr(field, "vocabulary"): # preset a vocabulary on self.context, or reset with one off current # context if the one preset previously was off a different context if (field.vocabulary is None or ( # a vocab was preset previously hasattr(field, "_vtf_last_context") and # and it was prepared with a different context getattr(field, "_vtf_last_context") is not self.context) ): from bungeni.alchemist.utils import get_vocabulary field.vocabulary = get_vocabulary(field.vocabularyName)(self.context) # remember last context used to preset vocabulary field._vtf_last_context = self.context log.debug("Validation of vocabulary field (%r, %r) with value=%r -- " "stuffing vocabulary instance %s [context: %r] onto field %s ...", name, field.vocabularyName, value, field.vocabulary, self.context, field) ''' # !+VTF a "temporary" and simpler version of above, to avoid # AttributeError in try block below (and incorrectly failed validation). TMP_SET_VOCAB = False if hasattr(field, "vocabulary") and field.vocabulary is None: TMP_SET_VOCAB = True from bungeni.alchemist.utils import get_vocabulary field.vocabulary = get_vocabulary(field.vocabularyName)(self.context) # standard field validation !+ necessary, already called elsewhere? try: widget_error = field.validate(value) except (RequiredMissing,) as exc: widget_error = exc # !+ other possible exceptions, should never pass here? except (formlib.form.NoInputData, interface.Invalid, Exception,) as exc: widget_error = exc probing.log_exc(sys.exc_info(), log_handler=log.error) log.debug(probing.class_inheritance(exc)) log.error("\n" " %r.validate(%r) FAILED (field %r)\n" " [context: %r]", field, value, name, self.context) #raise # !+? # !+VTF clear any vocabulary that we preset above if TMP_SET_VOCAB: field.vocabulary = None if widget_error: errors.append(self.set_widget_error(name, widget_error)) widget_error = None continue # get db column definition domain_attr = getattr(dm, name) if not isinstance(domain_attr, sa.orm.attributes.InstrumentedAttribute): continue domain_attr_property = domain_attr.property assert isinstance(domain_attr_property, sa.orm.properties.ColumnProperty), name # !+TMP sanity check # !+MULTIPLE_COLUMNS only single columns supported (so far) if len(domain_attr_property.columns) == 1: col = domain_attr_property.columns[0] #!+AttributeError: '_Label' object has no attribute 'nullable' # not: sa.sql.expression._Label, sa.sql.expression.ColumnElement assert isinstance(col, sa.schema.Column), col else: log.warn("SQLAlchemy property %r NOT defined as a single column, " "skipping derived_table_schema validation...", name) continue # validate against db column definition # nullable if value is None: if not col.nullable: errors.append(self.set_widget_error(name, _(u"May not be null"))) continue # sa.String if isinstance(col.type, sa.types.String): # length length = col.type.length if length is not None: if length < len(value): errors.append(self.set_widget_error(name, _(u"May not be longer than ${length}", mapping={"length": length}))) return errors
def validate_derived_table_schema(self, action, data): """Look-ahead validate against database contraints. """ dm = self.domain_model ti = capi.get_type_info(dm) derived_table_schema = ti.derived_table_schema errors = [] for name in derived_table_schema: ff = self.form_fields.get(name) # skip if no form does not define a corresponding form field if not ff: continue # !+ skip if field not included in this form "mode"? #if name not in getattr("%s_columns" % (self.mode), ti.descriptor_model): # continue field = derived_table_schema.get(name) assert field is ff.field, name # !+TMP sanity check value = data.get(name, None) ''' !+VTF disabling for now, as attempting to preset a contextualized vocab fails for subsequent lodaing of a *view*, as this is executed when validating a *submit* -- may still work if executed right moment. # !+VTF bungeni.ui.fields.VocabularyTextField -- here a vocabulary # field is not necessarily the type defined by bungeni # e.g. zope.schema._field.Choice that during validation attempts to # retrieve the vocabulary and initialize it with a None context... # To preempt the error that this causes, we check for and stuff # an appropiately initialized vocabulary instance onto this field... if hasattr(field, "vocabulary"): # preset a vocabulary on self.context, or reset with one off current # context if the one preset previously was off a different context if (field.vocabulary is None or ( # a vocab was preset previously hasattr(field, "_vtf_last_context") and # and it was prepared with a different context getattr(field, "_vtf_last_context") is not self.context) ): from bungeni.alchemist.utils import get_vocabulary field.vocabulary = get_vocabulary(field.vocabularyName)(self.context) # remember last context used to preset vocabulary field._vtf_last_context = self.context log.debug("Validation of vocabulary field (%r, %r) with value=%r -- " "stuffing vocabulary instance %s [context: %r] onto field %s ...", name, field.vocabularyName, value, field.vocabulary, self.context, field) ''' # !+VTF a "temporary" and simpler version of above, to avoid # AttributeError in try block below (and incorrectly failed validation). TMP_SET_VOCAB = False if hasattr(field, "vocabulary") and field.vocabulary is None: TMP_SET_VOCAB = True from bungeni.alchemist.utils import get_vocabulary field.vocabulary = get_vocabulary(field.vocabularyName)( self.context) # standard field validation !+ necessary, already called elsewhere? try: widget_error = field.validate(value) except (RequiredMissing, ) as exc: widget_error = exc # !+ other possible exceptions, should never pass here? except ( formlib.form.NoInputData, interface.Invalid, Exception, ) as exc: widget_error = exc probing.log_exc(sys.exc_info(), log_handler=log.error) log.debug(probing.class_inheritance(exc)) log.error( "\n" " %r.validate(%r) FAILED (field %r)\n" " [context: %r]", field, value, name, self.context) #raise # !+? # !+VTF clear any vocabulary that we preset above if TMP_SET_VOCAB: field.vocabulary = None if widget_error: errors.append(self.set_widget_error(name, widget_error)) widget_error = None continue # get db column definition domain_attr = getattr(dm, name) if not isinstance(domain_attr, sa.orm.attributes.InstrumentedAttribute): continue domain_attr_property = domain_attr.property assert isinstance( domain_attr_property, sa.orm.properties.ColumnProperty), name # !+TMP sanity check # !+MULTIPLE_COLUMNS only single columns supported (so far) if len(domain_attr_property.columns) == 1: col = domain_attr_property.columns[0] #!+AttributeError: '_Label' object has no attribute 'nullable' # not: sa.sql.expression._Label, sa.sql.expression.ColumnElement assert isinstance(col, sa.schema.Column), col else: log.warn( "SQLAlchemy property %r NOT defined as a single column, " "skipping derived_table_schema validation...", name) continue # validate against db column definition # nullable if value is None: if not col.nullable: errors.append( self.set_widget_error(name, _(u"May not be null"))) continue # sa.String if isinstance(col.type, sa.types.String): # length length = col.type.length if length is not None: if length < len(value): errors.append( self.set_widget_error( name, _(u"May not be longer than ${length}", mapping={"length": length}))) return errors