Python Dataclass - getting metadata from attribute name - python-3.x

I have a Dataclass that looks like this:
#dataclass
class Example:
smtng: int = field(init=True, metadata={'int_name':"Thing",'ext_name':"it"})
smtng_else: str = field(init=True, metadata={'int_name':"other",'ext_name':"that"})
as you can see the metadata dict has external and internal name fields
I would like to access these through functions E.g. get_ext_name(attribute_name) -> which would return an attribute's name under the metadata dict "ext_name"
is there a sleek way to to this?
Thanks

So I found this way to do it thanks to vibhu4agarwal on GeeksForGeeks.
Adding this method to the class works:
def ext_name(self, attribute_name):
return self.__dataclass_fields__[attribute_name].metadata['ext_name'])
if there's a better workaround I'd love to see it
here's a link to the article:
https://www.geeksforgeeks.org/data-classes-in-python-set-3-dataclass-fields/

Related

Extract type hints for object attributes in Python [duplicate]

I want to get the type hints for an object's attributes. I can only get the hints for the class and not an instance of it.
I have tried using foo_instance.__class__ from here but that only shows the class variables.
So in the example how do I get the type hint of bar?
class foo:
var: int = 42
def __init__(self):
self.bar: int = 2
print(get_type_hints(foo)) # returns {'var': <class 'int'>}
I just had the same problem. The python doc isn't that clear since the example is made with what is now officially called dataclass.
Student(NamedTuple):
name: Annotated[str, 'some marker']
get_type_hints(Student) == {'name': str}
get_type_hints(Student, include_extras=False) == {'name': str}
get_type_hints(Student, include_extras=True) == {
'name': Annotated[str, 'some marker']
}
It give the impression that get_type_hints() works on class directly. Turns out get_type_hints() returns hints based on functions, not on class. That way it can be use with both if we know that. A normal class obviously not being instantiated at it's declaration, it does not have any of the variables set within the __init__() method who hasn't yet been called. It couldn't be that way either if we want the possibility to get the type hints from class-wide variables.
So you could either call it on __init__(), that is if variables are passed in arguments though (yes i seen it's not in your example but might help others since i didn't seen this anywhere in hours of search);
class foo:
var: int = 42
def __init__(self, bar: int = 2):
self.bar = int
print(get_type_hints(foo.__init__))
At last for your exact example i believe you have two choices. You could instantiate a temporary object and use del to clean it right after if your logic allows it. Or declare your variables as class ones with or without default values so you can get them with get_type_hints() and assign them later in instantiations.
Maybe this is a hack, and you have to be the creator of your instances, but there are a subset of cases in which using a data class will get you what you want;
Python 3.7+
#dataclass
class Foo:
bar: str = 2
if __name__ == '__main__':
f = Foo()
print(f.bar)
print(get_type_hints(f))
2
{'bar': <class 'str'>}
Hints only exist at the class level — by the time an instance is created the type of its attributes will be that of whatever value has been assigned to them. You can get the type of any instance attribute by using the first form of the built-in type() function — e.g. type(foo_instance.var).
This information isn't evaluated and only exists in the source code.
if you must get this information, you can use the ast module and extract the information from the source code yourself, if you have access to the source code.
You should also ask yourself if you need this information because in most cases reevaluating the source code will be to much effort.

Sphinx: Share function Docstring [duplicate]

I have a class like this:
class MyBase(object):
x = 3
"""Documentation for property x"""
and another class that inherits it:
class MyObj(MyBase):
x = 0
When I use sphinx's autodoc to generate documentation, MyObj.x is not documented. Is there any way to inherit the docstring from MyBase.x? I found DocInherit but since this uses a decorator, it only works for class methods. Any way to do this with properties?
I found a workaround using the property function:
class MyBase(object):
_x = 3
x = property( lambda s: s._x, doc="Documentation for property x")
class MyObj(MyBase):
_x = 0
This is nice in that given an instance variable:
>>> m = MyObj()
>>> m.x
0
one can call help(m) and get proper documentation of property x and sphinx also picks this up correctly.
As far as I know, docstrings for attributes are not part of Python. When I try it, MyBase.x.__doc__ does not get set to the string beneath it. Docstrings only work on classes, functions and methods. If Sphinx picks up the string underneath x = 3 as a docstring, it's probably doing its own processing of the source code to get that.
If you only care for building Documentation via Sphinx. you can use:
":inherited-members:"
.. autoclass:: Noodle
:members:
:inherited-members:
This will also add the doc strings of inherited members in Sphinx Documentation.
http://sphinx-doc.org/ext/autodoc.html
As Thomas already stated, attributes do not have docstrings in Python. Sphinx however provides it's own processing allowing for attributes to be documented.
class Test(object):
#: This is an attibute docstring.
test_attr = 'test'
#property
def test_prop(self):
"""This is a property docstring."""
This results in:
class Test
Bases: object
test_attr = 'test'
This is an attibute docstring.
test_prop
This is a property docstring.

TypeError: 'list' object is not callable - class coding

I am trying to code a class, in python. I think the answer to my question is probably very simple but i am new to code here is the code:
The Actual Code
Once run
I dont see anything wrong, is there something simple i have missed?
the method name and the list name both are actions. when its initialized self.actions ( method) is replaced by self.actions (list). list is not callable
class Troll:
def __init__(self):
self.name = ''
self.phrases = ['Pass the ball','SHOOT']
self.available_actions = ['passes the ball','shoots']
def speak(self):
print(self.name, "shouts", random.choice(self.phrases))
def actions(self):
print(self.name,"decides to", self.available_actions)
In your case method name overrides variable name because both are same. So either change method name or variable name.

How to define the same field for load_only and dump_only params at the Marshmallow scheme?

I am trying to build a marshmallow scheme to both load and dump data. And I get everything OK except one field.
Problem description
(If you understand the problem, you don't have to read this).
For load data its type is Decimal. And I used it like this before. Now I want to use this schema for dumping and for that my flask API responses with: TypeError: Object of type Decimal is not JSON serializable. OK, I understand. I changed the type to Float. Then my legacy code started to get an exception while trying to save that field to database (it takes Decimal only). I don't want to change the legacy code so I looked for any solution at the marshmallow docs and found load_only and dump_only params. It seems like those are what I wanted, but here is my problem - I want to set them to the same field. So I just wondered if I can define both fields and tried this:
class PaymentSchema(Schema):
money = fields.Decimal(load_only=True)
money = fields.Float(dump_only=True)
I have been expected for a miracle, of course. Actually I was thinking that it will skip first definition (correctly, re-define it). What I got is an absence of the field at all.
Workaround solution
So I tried another solution. I created another schema for dump and inherit it from the former schema:
class PaymentSchema(Schema):
money = fields.Decimal(load_only=True)
class PaymentDumpSchema(PaymentSchema):
money = fields.Float(dump_only=True)
It works. But I wonder if there's some another, native, "marshmallow-way" solution for this. I have been looking through the docs but I can't find anything.
You can use the marshmallow decorator #pre_load in this decorator you can do whatever you want and return with your type
from marshmallow import pre_load
import like this and in this you will get your payload and change the type as per your requirement.
UPD: I found a good solution finally.
NEW SOLUTION
The trick is to define your field in load_fields and dump_fields inside __init__ method.
from marshmallow.fields import Integer, String, Raw
from marshmallow import Schema
class ItemDumpLoadSchema(Schema):
item = Raw()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not (self.only and 'item' not in self.only) and \
not (self.exclude and 'item' in self.exclude):
self.load_fields['item'] = Integer(missing=0)
self.dump_fields['item'] = String()
Usage:
>>> ItemDumpLoadSchema().load({})
{'item': 0}
>>> ItemDumpLoadSchema().dump({'item': 0})
{'item': '0'}
Don't forget to define field in a schema with some field (Raw in my example) - otherwise it may raise an exception in some cases (e.g. using of only and exclude keywords).
OLD SOLUTION
A little perverted one. It based on #prashant-suthar answer. I named load field with suffix _load and implemented #pre_load, #post_load and error handling.
class ArticleSchema(Schema):
id = fields.String()
title = fields.String()
text = fields.String()
class FlowSchema(Schema):
article = fields.Nested(ArticleSchema, dump_only=True)
article_load = fields.Int(load_only=True)
#pre_load
def pre_load(self, data, *args, **kwargs):
if data.get('article'):
data['article_load'] = data.pop('article')
return data
#post_load
def post_load(self, data, *args, **kwargs):
if data.get('article_load'):
data['article'] = data.pop('article_load')
return data
def handle_error(self, exc, data, **kwargs):
if 'article_load' in exc.messages:
exc.messages['article'] = exc.messages.pop('article_load')
raise exc
Why the old solution is not a good solution?
It doesn't allow to inheritate schemas with different handle_error methods defined. And you have to name pre_load and post_load methods with different names.
pass data_key argument to the field definition
Documentation mentions, data_key parameter can be used along with dump_only or load_only to be able to have same field with different functionality.
So you can write your schema as...
class PaymentSchema(Schema):
decimal_money = fields.Decimal(data_key="money", load_only=True)
money = fields.Float(dump_only=True)
This should solve your problem. I am using data_key for similar problem in marshmallow with SQLAlchemyAutoSchema and this fixed my issue.
Edit
Note: The key in ValidationError.messages (error messages) will be decimal_money by default. You may tweak the handle_error method of Schema class to replace decimal_money with money but it is not recommended as you yourself may not be able to differentiate between the error messages fields.
Thanks.

How to store in variable function returning value (kivy properties)

class Data(object):
def get_key_nicks(self):
'''
It returns key and nicks object
'''
file = open(self.key_address, 'rb')
key = pickle.load(file)
file.close()
file = open(self.nicks_address, 'rb')
nicks = pickle.load(file)
file.close()
return (key, nicks)
Above is the data api and function which i want to use in kivy
class MainScreen(FloatLayout):
data = ObjectProperty(Data())
key, nicks = ListProperty(data.get_key_nicks())
it gives error like: AttributeError: 'kivy.properties.ObjectProperty' object has no attribute 'get_key_nicks'
Properties are descriptors, which basically means they look like normal attributes when accessed from instances of the class, but at class level they are objects on their own. That's the nature of the problem here - at class level data is an ObjectProperty, even though if you access it from an instance of the class you'll get your Data() object that you passed in as the default value.
That said, I don't know what your code is actually trying to do, do you want key and nicks to be separate ListProperties?
Could you expand a bit more on what you're trying to do?
I think all you actually need to do is:
class MainScreen(FloatLayout):
data = ObjectProperty(Data())
def get_key_nicks(self):
return data.get_key_nicks()

Resources