I would like to use the python setter function (eventually to stick metadata to an object). the method works when the complete attribute changes, but it does not when an attributes' attribute changes:
MWE
class MyObject():
def __init__(self, props):
self.props = props
#property
def props(self):
return self._props
#props.setter
def props(self, p):
print("setter called!")
self._props = p
1 - Initialize object:
myobject = MyObject(props={"name": "API"})`
setter called!
2 - Change attribute props:
myobject.props = {"name": "anyone else"}
setter called!
3 - Change attributes' properties:
myobject.props['name'] = "yet anyone else"
and this does not run the setter
In third situation it does not work as you think it should.
In [1]: class MyObject():
...: def __init__(self, props):
...: self.props = props
...:
...: #property
...: def props(self):
...: print("property called!")
...: return self._props
...:
...: #props.setter
...: def props(self, p):
...: print("setter called!")
...: self._props = p
...:
In [2]: myobject.props['name'] = "yet anyone else"
property called!
Setter is called when you try assign new object to props. In case which you provided, firstly you get dictionary attached to props via property method and then set new value for one of keys. Setter will not work for changing mutable objects inside.
You've probably need to provide your own class for props or use observer pattern. If you need more help provide more detailed example.
Related
I have a function that runs some code if the object has a certain attribute, and in rare cases if if the object does not have the attribute, it runs different code. It is hard for me to create the object without the attribute for testing. I tried del instance.attribute but got an error. The attribute is actually a #property under the hood.
I have an object instance that has foo attribute. How does one mock it so that when one tries to access instance.foo it raises an AttributeError as usual if there is no attribute?
I tried mock.Mock(side_effect=AttributeError('Boom!')) but it only works with methods.
You could try to use a PropertyMock for the property, and generally you shall be able to set the respective side effect. Here is a simple working example:
from unittest import mock
import pytest
class Foo:
#property
def bar(self):
return "bar"
def test_no_foo():
bar_mock = mock.PropertyMock()
with mock.patch(f"{__name__}.Foo.bar", bar_mock):
bar_mock.side_effect = AttributeError('Boom!')
foo = Foo()
with pytest.raises(AttributeError):
foo.bar
As you patch the property in the class, not in the object, you can can also do this using patch.object if you have access to the object by accessing the class of the object:
def test_no_foo():
bar_mock = mock.PropertyMock()
foo = Foo()
with mock.patch.object(foo.__class__, "bar", bar_mock):
bar_mock.side_effect = AttributeError('Boom!')
with pytest.raises(AttributeError):
foo.bar
FastAPI shows that you can set response_model_exclude_none=True in the decorator to leave out fields that have a value of None: https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter
I would like to do this, but the None field I want to exclude is nested within the parent response model. I.e.
class InnerResponse(BaseModel):
id: int
name: Optional[str] = None
class Response(BaseModel):
experience: int
prices: List[InnerResponse]
#app.post("/dummy", response_model=apitypes.Response, response_model_exclude_none=True)
async def backend_dummy(payload: apitypes.Request):
...
However, when I get a response back, it the "prices" list here still has InnerResponses that have "name": null.
Is there a way to apply the exclude rule on nested models?
A possibility will be to create a class that inherits from BaseModel and override the dict method:
from pydantic import BaseModel
class AppModel(BaseModel):
def dict(self, *args, **kwargs):
if kwargs and kwargs.get("exclude_none") is not None:
kwargs["exclude_none"] = True
return BaseModel.dict(self, *args, **kwargs)
Now, create the classes from AppModel:
class InnerResponse(AppModel):
id: int
name: Optional[str] = None
class Response(AppModel):
experience: int
prices: List[InnerResponse]
For anyone who finds this while searching: The code above works fine, but my issue was another endpoint outside of this code block that did not have the response_model_exclude_none=True set on it. Every endpoint that needs to exclude those "None" values needs to have that set.
I've been using a schema behavior with no problem, but I want to also have a method that provides some logic. Right now I have
class IMyFoo(form.Schema):
requester = schema.TextLine(title=_(u"Requestor"),
required=False,
)
def foo(self):
""" foo """
alsoProvides(IMyFoo, IFormFieldProvider)
And in the zcml
<plone:behavior
title="My behavior"
description="Some desc"
provides=".behaviors.IMyFoo"
for=".interfaces.ISomeInterface"
/>
I included IMyFoo in the behaviors section of a content type in portal_types. This gives me the schema but not that foo() method. So I tried to add a factory for it from reading http://plone-training.readthedocs.org/en/latest/behaviors2.html with the following code
class MyFoo(object):
def foo(self):
return 'bar'
And in the zcml
<plone:behavior
title="My behavior"
description="Some desc"
provides=".behaviors.IMyFoo"
factory=".behaviors.MyFoo"
for=".interfaces.ISomeInterface"
/>
But this didn't seem to make a difference, or at least, I don't know how to access that method. The closest I've been able to come is the following:
class IMyFoo(Interface):
""" Marker """
class MyFoo(object):
def __init__(self, context):
self.context = context
def foo(self):
return 'bar'
<adapter for=".interfaces.ISomeInterface"
factory=".behaviors.MyFoo"
provides=".behaviors.IMyFoo" />
I put IMyFoo in the behaviors attribute in the fti, and then call it by walking through all behaviors with something like
behavior = resolveDottedName(context.portal_types.getTypeInfo(context.portal_type).behaviors[-1]))
behavior(self).myfoo()
Surely going through the FTI like that is not the proper way to do it. But I'm at a loss at this point. In Archetypes I would just make a mixin class and inherit it with any content type I wanted to use it. I could do the same here, but my understanding is that behaviors are supposed to be a replacement for them, so I'd like to figure out how to use this preferred method.
As you've discovered, the schema class really is just an interface. It can't provide any methods. To provide more functionality, you need to connect your behavior interface to a factory class that adapts a dexterity object to provide your interface.
So, if your behaviors.py looks like this:
# your imports plus:
from plone.dexterity.interfaces import IDexterityContent
from zope.component import adapts
from zope.interface import implements
class IMyFoo(form.Schema):
requester = schema.TextLine(
title=_(u"Requestor"),
required=False,
)
def foo(self):
""" foo """
alsoProvides(IMyFoo, IFormFieldProvider)
class MyFoo(object):
implements(IMyFoo)
adapts(IDexterityContent)
def __init__(self, context):
self.context = context
def foo(self):
return 'bar'
Then your one-and-only zcml declaration would be:
<plone:behavior
title="My behavior name"
description="Behavior description"
provides=".behavior.IMyFoo"
factory=".behavior.MyFoo"
for="plone.dexterity.interfaces.IDexterityContent"
/>
And, you would reach your method with code like:
IMyFoo(myFooishObject).foo()
Note the use of IDexterityContent. You're creating a behavior that could be applied to any Dexterity content. So, the behavior adapter should be for that very general interface.
I'm trying to use macro annotations in scala, where my macro annotation would take an argument of another type. It would then use scala reflection to look at the passed in type, and add some methods as appropriate.Eg.
trait MyTrait {
def x: Int
def y: Float
}
#MyAnnotation class MyClass //<-- somehow, this annotation should reference MyTrait
class MyAnnotation(val target: Any) extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro MyAnnotationImpl.impl
}
object MyAnnotationImpl {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
// if I can get a handle on the type MyTrait in here
// then I can call .members on it, etc.
...
}
}
Basically, the same thing as Using Scala reflection in Scala macros, except using macro annotations. However, when I try to template my macro annotation with a TypeTag
class MyAnnotation[T](val target: Any) extends StaticAnnotation {
def macroTransform[T](annottees: Any*) = macro MyAnnotationImpl.impl[T]
}
object MyAnnotationImpl {
def impl[T: c.WeakTypeTag](c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
...
}
}
I get
[error] /Users/imran/other_projs/learn_macros/macros/src/main/scala/com/imranrashid/oleander/macros/MacrosWithReflection.scala:7: macro annotation has wrong shape:
[error] required: def macroTransform(annottees: Any*) = macro ...
[error] found : def macroTransform[T](annottees: Any*) = macro ...
[error] class MyAnnotation[T](val target: Any) extends StaticAnnotation {
[error] ^
I've also tried to make the type an argument to my annotation, so I would use it like #MyAnnotation(MyTrait) class Foo .... I can extract the name as a String with something like
val targetTrait = c.prefix.tree match {
case Apply(Select(New(Ident(_)), nme.CONSTRUCTOR), List(Ident(termName))) => termName
}
but, I'm not sure what I can do w/ that String to get back the full type. I've also tried variants like #MyAnnotation(typeOf[MyTrait]) class Foo ..., and then use c.eval on the typeOf inside my macro, but that doesn't compile either.
In macro paradise 2.0.0-SNAPSHOT we have quite a tricky way of accessing type parameters for macro annotations (the situation will improve later on when we have dedicated APIs for that, but right now it's very difficult to introduce new functionality to scala-reflect.jar in macro paradise, so the current API is a bit rough).
For now it's necessary to specify the type parameter on the annotation class and not to declare any type parameters on the macroTransform method. Then, in macro expansion, access c.macroApplication and extract the untyped tree corresponding to the passed type parameter. Afterwards, do c.typeCheck as described in Can't access Parent's Members while dealing with Macro Annotations.
As Eugene points out in his answer it is possible to match on the tree of the whole macro application. Like every Scala method, annotation macro applications can take multiple type argument lists as well as multiple value argument lists.
Consider the macro application of an annotation macro called test:
#test[A, B][C, D](a, b)(c, d) trait Foo
In the implementation of test we can inspect the macro application by
println(show(c.macroApplication))
which will result in:
new test[A, B][C, D](a, b)(c, d).macroTransform(abstract trait Foo extends scala.AnyRef)
To extract the (type/value) parameters from the tree you have to pattern match on the tree. A parser for an arbitrary amount of parameter lists can be found in this project
Using this parser retrieving the first value argument of the macro application is as easy as
val List(List(arg)) = MacroApp(c.macroApplication).termArgs
I'm trying to use DataBindingUtils.bindObjectToInstance(object, source) to copy properties from one object to another using this code (which can be run in the Grails console):
import org.codehaus.groovy.grails.web.binding.DataBindingUtils
class Source {
String foo = 'foo'
String bar = 'bar'
}
class Target {
String foo
String bar
}
def s = new Source()
def t = new Target()
def result = DataBindingUtils.bindObjectToInstance(t, s)
assert t.foo == 'foo'
assert t.bar == 'bar'
But the assertions are failing because the properties of t are null, why?
The data binder (below) in Grails has only implemented binding for a select subset of object types, namely Maps and Web Requests. So binding two arbitrary objects isn't implemented.
https://github.com/grails/grails-core/blob/master/grails-web/src/main/groovy/org/codehaus/groovy/grails/web/binding/DataBindingUtils.java