How to override class attribute access in python3 - python-3.x

I am trying to override class attribute access in python3. I found this question already answered for python2. But the same is not working with Python3. Please help me to understand why this does not work with Python3 and how to get it to work.
Here is the code i am trying to verify in Python3:
class BooType(type):
def __getattr__(self, attr):
print(attr)
return attr
class Boo(object):
__metaclass__ = BooType
boo = Boo()
Boo.asd #Raises AttributeError in Python3 where as in Python2 this prints 'asd'

from http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Metaprogramming.html
Python 3 changes the metaclass hook. It doesn’t disallow the __metaclass__ field, but it ignores it. Instead, you use a keyword argument in the base-class list:
in your case, you have to change to:
class Boo(object, metaclass = BooType):
pass
and that works. This syntax isn't compatible with python 2, though.
There's a way to create compatible code, seen in http://python-future.org/compatible_idioms.html#metaclasses
# Python 2 and 3:
from six import with_metaclass
# or
from future.utils import with_metaclass
class Boo(with_metaclass(BooType, object)):
pass

Related

Delegation pattern in Python using annotation

I have a class that should mimic behaviour of other class but with some flavour. For example Kotlin have delegation pattern (https://www.baeldung.com/kotlin/delegation-pattern) as a part of language to do such thing. But for Python when I try code below:
from dataclasses import dataclass
from typing import Generic, T
#dataclass
class Wrapper(T, Generic[T]):
__value__: T
def __getattr__(self, item):
return getattr(self.__value__, item)
# also need to delegate all magick methods
def __len__(self):
return self.__value__.__len__()
def try_some_funny_things(self):
setattr(__builtins__, "True", False)
funny_string = Wrapper[str]()
funny_string. # I want typehints as for str class here
I get the following error:
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
What are my final purposes:
Make PyCharm/Pylint (other typechecker) show fields/methods hints
Do not delegate all magic methods manually to value field
Any suggestion how can I do something like this in Python 3?

MyPy not considering dataclass attribute mechanics

I am developing a Python3.8 project with usage of typing and dataclasses, and automatic tests include mypy. This brings me in a strange behavior that I do not really understand...
In short: Mypy seems not to understand dataclass attributes mechanics that, to my understanding, make them instance attributes.
Here's a minimal example, with a package and two modules:
__init__.py: void
app_events.py:
class AppEvent:
pass
main.py:
import dataclasses
import typing
from . import app_events
class B:
"""Class with *app_events* as instance attribute."""
def __init__(self):
self.app_events: typing.List[app_events.AppEvent] = []
def bar(self) -> app_events.AppEvent:
# no mypy complaint here: the import is correctly distinguished
# from the attribute
...
class C:
"""Class with *app_events* as class attribute."""
app_events: List[app_events.AppEvent]
def chew(self) -> app_events.AppEvent:
# mypy considers app_events to be the class attribute
...
#dataclasses.dataclass
class D:
app_events: typing.List[app_events.AppEvent] = \
dataclasses.field(default_factory=list)
def doo(self) -> app_events.AppEvent:
# same here: mypy considers app_events to be the class attribute
...
And the typecheck result:
PyCharm complains, for methods C.chew and D.doo: Unresolved attribute reference 'AppEvent' for class 'list'
mypy complains, still for methods C.chew and D.doo, that error: Name 'app_events.AppEvent' is not defined.
No issue for B.bar as written, though if app_events attribute is declared as a class attribute (instead of being defined in self.__init__, then mypy raise the same complaint.)
-> any idea how to understand/solve/circumvent this elegantly?
I'd really like not to rename my module and attributes, but if you have nice names in mind, please do not hesitate to propose :-)

Python3 iteratively call members of class with reflection

Using Python3, I am trying to call the members of a class which contain is_ or has_. Their returns are hard coded to True.
Here's what I've implemented, I don't know how to actually call the stored members using the black_magic method. Can anybody please help? Many thanks.
import inspect
import unittest
class Foo:
def is_bar(self):
print(bar)
return True
def is_baz(self):
print(baz)
return True
class MyTest(unittest.TestCase):
def setUp(self):
self.object=Foo()
self.object_members=inspect.getmembers(self.object)
def test_assertions_when_typical(self):
self.assertEqual(True,True)
for member in self.object_members:
member_name, member_address = member
if "is_" in member_name or "has_" in member_name:
result=black_magic(member)
self.assertEqual(result,True)
if __name__=='__main__':
unittest.main()
I'm invoking this with:
python3 -m unittest test_python_reflection_minimal_example.py
You can use the following implementation, which works locally:
def black_magic(self, objectClass, member):
return getattr(objectClass, member[0])()
and call it using result=self.black_magic(self.object, member).
Explanation: member is a tuple containing the method name and method reference. We then use this name and invoke the method on the self.object.

Code incompatibility issues - Python 2.x/ Python 3.x

I have this code:
from abc import ABCMeta, abstractmethod
class Instruction (object):
__metaclass__ = ABCMeta
def __init__(self, identifier_byte):
#type: (int) ->
self.identifier_byte = identifier_byte
#abstractmethod
def process (self):
print ("Identifier byte: ()".format(self.identifier_byte))
class LDAInstruction (Instruction):
def process (self):
super(Instruction,self).process()
with works fine with Python 3.2 but not with 2.6. Then based on this topic: TypeError: super() takes at least 1 argument (0 given) error is specific to any python version?
I changed the last line to:
super(Instruction,self).process()
which causes this error message on this precise line:
AttributeError: 'super' object has no attribute 'process'
For me it seems that there is a "process" method for the super invocation. Is Python saying that "super" is an independent object, unrelated to instruction? If yes, how can I tell it that super shall only invoke the base class constructor?
If not, how I shall proceed? Thanks for any ideas.
You're passing the wrong class to super in your call. You need to pass the class you're making the call from, not the base class. Change it to this and it should work:
super(LDAInstruction, self).process()
It's unrelated to your main error, but I'd further note that the base-class implementation of process probably has an error with its attempt at string formatting. You probably want {0} instead of () in the format string. In Python 2.7 and later, you could omit the 0, and just use {}, but for Python 2.6 you have to be explicit.

Using HTMLParser in Python 3.2

I have been using HTML Parser to scrapping data from websites and stripping html coding whilst doing so. I'm aware of various modules such as Beautiful Soup, but decided to go down the path of not depending on "outside" modules. There is a code code supplied by Eloff: Strip HTML from strings in Python
from HTMLParser import HTMLParser
class MLStripper(HTMLParser):
def __init__(self):
self.reset()
self.fed = []
def handle_data(self, d):
self.fed.append(d)
def get_data(self):
return ''.join(self.fed)
def strip_tags(html):
s = MLStripper()
s.feed(html)
return s.get_data()
It works in Python 3.1. However, I recently upgraded to Python 3.2.x and have found I get errors regarding the HTML Parser code as written above.
My first error points to the line:
s.feed(html)
... and the error says ...
AttributeError: 'MLStripper' object has no attribute 'strict'
So, after a bit of research, I add "strict=True" to the top line, making it...
class MLStripper(HTMLParser, strict=True)
However, I get the new error of:
TypeError: type() takes 1 or 3 arguments
To see what would happen, I removed the "self" argument and left in the "strict=True"... which gave up the error:
NameError: global name 'self' is not defined
... and I got the "I'm guessing on guesses" feeling.
I have no idea what the third argument in the class MLStripper(HTMLParser) line would be, after self and strict=True; research didn't toss any enlightenment.
You're subclassing HTMLParser, but you aren't calling its __init__ method. You need to add one line to your __init__ method:
def __init__(self):
super().__init__()
self.reset()
self.fed = []
Also, for Python 3, the import line is:
from html.parser import HTMLParser
With these changes, a simple example works. Don't change the class line, that's not related.

Resources