ClassSecurityInfo.declarePrivate() etc. as Python decorators - plone

Does declarePrivate() et. al. Zope 2 style access control declarations have any modern Python syntax method decorator?
E.g.
class EncryptedField(StringField):
"""
"""
security = ClassSecurityInfo()
# Something more nice here? like #private?
security.declarePrivate('get')
def get(self, instance, **kwargs):
value = ObjectField.get(self, instance, **kwargs)
if getattr(self, 'raw', False):
return value

Nope, we are still stuck with this syntax.
They cannot be converted to decorators so easily because they are a class-level declaration, not actually attached to the methods. You'd need to play nasty tricks with the calling frame to get back to the class declaration and the security object to make this work.
Thus, there is a step where the security object is processed to turn the declarations into class attributes that Zope2 recognizes; this step is these days automatically taken care of by the archetypes registerType call.

Related

Pydantic type syntax explanation

In the code below from the official FastAPI tutorials page, I can't wrap my head around the the statements (e.g, name: str).
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
app = FastAPI()
#app.post("/items/")
async def create_item(item: Item):
return item
I mean, I understand that they should enforce the type, but how exactly should they do that, as python does not enforce types.
I saw also the Pydantic manual, but have seen no explanation regarding this particular syntax (e.g, name: str) in the class declaration.
Could someone please explain this syntax for me, and how can you check for the meant type of a class when you create it in such a way?
Thanks in advance.
Python's typing behaviour
Typing is supported above Python 3.5: https://docs.python.org/3/library/typing.html.
If your type hint is incorrect, your code will still run - as the documentation says: it is only a type hint. Python is still a dynamically typed language.
You can use MyPy however, to catch these errors before runtime.
Pydantic's typing behaviour
Though it is also important to understand Pydantic's behaviour: if your type is not str, but a conversion is possible, it will convert to string first without any error messages. Otherwise, it will raise an error.
If you want to enforce raising an error without conversion, you should use Pydantic strict types: https://pydantic-docs.helpmanual.io/usage/types/#strict-types
But here is what Pydantic docs are telling you:
"[...] annotation-only declaration tells pydantic that this field is required. Strings, bytes or floats will be coerced to ints if possible; otherwise an exception will be raised."
(https://pydantic-docs.helpmanual.io/)
In Python 3.5 they introduced the type hints and it follows a specific syntax (see PEP-484 and PEP-3107).
It states that,
PEP 3107 introduced syntax for function annotations, but the semantics were deliberately left undefined. There has now been enough 3rd party usage for static type analysis that the community would benefit from a standard vocabulary and baseline tools within the standard library.
This means Python doesn't enforce the validation or static evaluation, but some 3rd party libraries will able to do that.
Coming to Pydatic's "validation enforcing technique", they wrote the logic in which how to evaluate the classes that inherit from the BaseModel
They've been calling the validators from the __init__(...) method itself and thus you will get the ValidationError exception if the input data doesn't meet the defined validation conditions.
In short, Pydatic's BaseModel is a normal Python class that takes the __init__(...) parameters and validate against the defined class variables.
The syntax “name: str” is a standard feature of Python from 3.6 upwards. It’s a type hint and doesn’t actually change the fundamental behaviour of Python underneath. The variable can have any value but this is a hint to remind you that you intended it to be a string. It also allows linters like mypy to flag that you are calling methods on this variable that do not exist on a str and therefore are likely to break at runtime. Finally it allows context sensitive editors to predict what methods are available because it has a hint of what type of variable this is which it normally wouldn’t in Python

How to use annotations with z3c.form's DictionaryField

There is documentation on using Python dict with z3c.form (loading & storing form data).
However, the z3c.form datamanager used for dicts is not registered for other types or interfaces (see reference), whereas annotations typically use something like PersistentDict.
How can I use the DictionaryField datamanager in this scenario? Ie. so that in my form's getContent method I merely return the PersistentDictannotation.
Well, unfortunately there seems no simple solution for this requirement.
I once faced the same problem using the datagrid field in a z3c form.
The following instruction solves the problem for the datagrid field, which is a list (PersistentList of dicts (PersistentMappings).
I guess you may adapt this solution for your case.
First you need to add the following code to the getContent method:
from plone.directives import form
class MyForm(form.SchemaEditForm):
schema = IMyFormSchema
ignoreContext = False
def getContent(self):
annotations = IAnnotations(self.context)
if ANNOTATION_KEY not in annotations:
annotations[ANNOTATION_KEY] = PersistentMapping()
return YourStorageConfig(annotations[ANNOTATION_KEY])
Important note: I wrap the annotation storage to satisfy the get/set behavior of the z3c form. Check the following YourStorageConfig implementation and you will see why :-).
class YourStorageConfig(object):
implements(IMyFormSchema)
def __init__(self, storage):
self.storage = storage
def __getattr__(self, name):
if name == 'storage':
return object.__getattr__(self, name)
value = self.storage.get(name)
return value
def __setattr__(self, name, value):
if name == 'storage':
return object.__setattr__(self, name, value)
if name == 'yourfieldname':
self.storage[name] = PersistentList(map(PersistentMapping, value))
return
raise AttributeError(name)
yourfieldname should be the field name you are using in the form schema.
To implement the a datagrid field, there is some more work to do, but this may be enough for your case.
Please post comments, or tracebacks, so I can provide further help. I'll gonna add more details/explanation if necessary ;-)
It turns out the answer is as easy as the following ZCML adapter registration:
<adapter
for="persistent.dict.PersistentDict zope.schema.interfaces.IField"
provides="z3c.form.interfaces.IDataManager"
factory="z3c.form.datamanager.DictionaryField"
/>
With that, the following customization of a form is sufficient to use (PersistentDict) annotations for loading & storing form data:
def getContent(self):
"return the object the form will manipulate (load from & store to)"
annotations = IAnnotations(self.context)
return annotations[SOME_ANNOTATIONS_KEY_HERE]
This is assuming that a PersistentDict has been previously stored at annotations[SOME_ANNOTATIONS_KEY_HERE] - otherwise the above code will result in KeyError. It would probably be a good idea to change above getContent so that if the annotation does not yet exist, it is created and initialized with some default values.
Finally, note that for some reason, z3c.form warns against enabling DictionaryField for every mapping type, so it may be prudent to for example subclass PersistentDict for form storage, rather than use it directly. I submitted an issue to z3c.form asking for clarification of that warning.

Querying adapters against plone.directives.form.Schema

I have a form model created as following:
from plone.app.directives import Form
class IFormSchema(form.Schema):
foobar = schema.Bool(title=u"Just another field")
I'd like to register an adapter against this definition:
#component.adapter(IFormSchema)
#interface.implementer(ITreeSelectURLProvider)
def TreeSourceURL():
"""
"""
return "http://foobar"
The registration goes correctly.
However, there is an issue that I don't know if IFormSchema is directly provided by any object in any point of z3c.form processing chain, so that I could call:
provider = ITreeSelectURLProvider(someObject)
Does IFormSchema get directly applied to some object (zope.interface.directlyProvides?) in any point of z3c.form or plone.autoform chain
If not, what is the recommended practice so that I can register adapters against the model? What classes I should make to implement this interface?
To make matters worse, the context in the question is not a real content item but a subform object.
Dexterity make sure that the schema interface (be that defined on the filesystem and referenced in the FTI, or defined through the web or in an XML file) is provided by instances of the content type.
This isn't really about forms, it's about Dexterity. The form.Schema base class is just a marker that extends Interface, and which allows some of plone.autoform's processing to take place at configuration time.

Anonymous users creating content types: which approaches instead of invokeFactory can be used?

I've come across this little function that let anonymous users call invoke factory.
security.declarePrivate('anonymousInvokeFactory')
def anonymousInvokeFactory(self, container, type_name, id,
REQUEST=None, *args, **kw):
"""
Anonymous cannot add objects with InvokeFactory, so this is a
special
method to do it with. Must be called from other function to limit
possibillities of abuse.
"""
# remember original user
mtool = getToolByName(self, 'portal_membership')
originalUser = mtool.getAuthenticatedMember()
# wrap the request in new security to be able to add content
user = self.getWrappedOwner()
newSecurityManager(REQUEST, user)
container.invokeFactory(type_name, id, REQUEST=REQUEST, *args, **kw)
# set original user again
newSecurityManager(REQUEST, originalUser)
return id
I seems perfect for a situation where I'm using some proxyManager metadata. But I haven't seen this little snippet anywhere besides this nabble entry - is it safe? Which disadvantages can you see in this approach? EDIT: I've found now in official community plone docs effort some references.
My scenario: the anonymous user is creating an Archetype object on ZODB, in a specific context only, that only accepts this type of object. He can not see any objects, he is just calling a form that is going to create these objects. These objects are going to be created, and their attributes (fields) need to be populated as well. The _createObjectType approach creates the object but it doesn't add the fields even using **kwargs. EDIT2: It's possible to edit using default acessors like obj.setTitle. I'm now using this approach, and it works flawlessly.
I would be weary of using anything that sets up a new security manager. A better way to do this would be to bypass security when creating the object.
You can do something like:
pt = getToolByName(context, 'portal_types')
type_info = pt.getTypeInfo('portal_type')
ob = type_info._constructInstance(context, id)
# CMFCore compatibility
if hasattr(type_info, '_finishConstruction'):
return type_info._finishConstruction(ob)
else:
return ob
source: uwosh.pfg.d2c

AS3/Flex Decorator Pattern

I'm trying to create a decorator class in AS3/Flex in order to add some functionality to a UI element. The problem is that I don't know how to automatically "redirect" method and property calls to the object being decorated. I suppose I'm looking for something like the __call() "magic method" from PHP, which is called every time the application calls a non-callable method.
So, the question is - how do I redirect calls for methods and properties (which aren't overriden/don't exist in the decorator class) to the object being decorated?
I think the closest to magic methods in actionscript would be extending Proxy.

Categories

Resources