Calling a class method from another class causes metaclass conflict - python

I have a Python web app and I want to define a general class or function for processing web pages and call it from a more specific class for specific page instances.
Error:
metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
I have checked all the StackOverflow questions & answers about that error message and did not understand the explanations given (and I found an article about metaclasses that I didn't understand - OOP is not really my thing). I also looked at all the StackOverflow questions & answers about calling a class method from another class (and didn't understand them either).
Code:
import webapp2
from datetime import datetime, timedelta
from google.appengine.ext import ndb
from google.appengine.api import users
import admin_templates
import authenticate
class myPage(webapp2.RequestHandler):
def get(pageInstance):
pageInstance.outputHtml()
def outputHtml(pageInstance,pageTitle,pageContent):
adminLink = authenticate.get_adminlink()
authMessage = authenticate.get_authmessage()
adminNav = authenticate.get_adminnav()
pageInstance.response.headers['Content-Type'] = 'text/html'
html = admin_templates.base
html = html.replace('#title#', pageTitle)
html = html.replace('#authmessage#', authMessage)
html = html.replace('#adminlink#', adminLink)
html = html.replace('#adminnav#', adminNav)
html = html.replace('#content#', pageContent)
pageInstance.response.out.write(html)
pageInstance.response.out.write(admin_templates.footer)
class AuditorsPage(webapp2.RequestHandler, myPage.outputHtml):
def get(self):
self.output_auditors()
def output_auditors(self):
self.outputHtml(admin_templates.auditors_title, admin_templates.auditors_content)
How do I call the outputHtml method in the myPage class from the AuditorsPage class without getting a metaclass error?

The error comes from the fact that you are subclassing your class from a method in the general class, which can't be done. Actually, your code is wrong, and there are at least two ways you can fix it:
1) Inherit from myPage instead: in this case, it won't work and will bring a MRO error because your methods are not part of any instance of the class, as they are not linked to the object using self as the first parameter.
This is the fix for your code in this case:
from google.appengine.ext import ndb
from google.appengine.api import users
import admin_templates
import authenticate
class myPage(webapp2.RequestHandler):
def get(self, pageInstance):
pageInstance.outputHtml()
def outputHtml(self, pageInstance,pageTitle,pageContent):
adminLink = authenticate.get_adminlink()
authMessage = authenticate.get_authmessage()
adminNav = authenticate.get_adminnav()
pageInstance.response.headers['Content-Type'] = 'text/html'
html = admin_templates.base
html = html.replace('#title#', pageTitle)
html = html.replace('#authmessage#', authMessage)
html = html.replace('#adminlink#', adminLink)
html = html.replace('#adminnav#', adminNav)
html = html.replace('#content#', pageContent)
pageInstance.response.out.write(html)
pageInstance.response.out.write(admin_templates.footer)
class AuditorsPage(myPage):
def get(self):
self.output_auditors()
2) Just get the two methods in the myPage class as normal methods with no class, and then call them directly from your class. Actually, you just need to create outputHtml() method, and then call it directly (without self.) from the method in your RequestHandler subclass.

Try this:
myPage.outputHtml()
instead of:
self.outputHtml()

Class AuditorsPage could inherit from myPage
import webapp2
from datetime import datetime, timedelta
from google.appengine.ext import ndb
from google.appengine.api import users
import admin_templates
import authenticate
class myPage(webapp2.RequestHandler):
def get(pageInstance):
pageInstance.outputHtml()
def outputHtml(pageInstance,pageTitle,pageContent):
adminLink = authenticate.get_adminlink()
authMessage = authenticate.get_authmessage()
adminNav = authenticate.get_adminnav()
pageInstance.response.headers['Content-Type'] = 'text/html'
html = admin_templates.base
html = html.replace('#title#', pageTitle)
html = html.replace('#authmessage#', authMessage)
html = html.replace('#adminlink#', adminLink)
html = html.replace('#adminnav#', adminNav)
html = html.replace('#content#', pageContent)
pageInstance.response.out.write(html)
pageInstance.response.out.write(admin_templates.footer)
class AuditorsPage(myPage):
def get(self):
self.output_auditors()
Then you can instanciate AuditorsPage() and call directly outputHtml
foo = AuditorsPage()
foo.outputHtml(admin_templates.auditors_title, admin_templates.auditors_content)

Ok so you are not calling super for the AuditorsPage:
class myPage():
def __init__(self):
pass
def outputHtml(self):
print("outputHTML")
class AuditorsPage(myPage):
def __init__(self):
super().__init__()
def output(self):
print("AuditorsPage")
I am using a very stripped down version of what you have to explain what is going on. As you can see, we have your myClass class. When creating an object from AuditorsPage, we are now able to access the method outputHTML as expected. Hope this helps.

Base on your question, I guess you want to inherit methods from myPage. If that is the case, instead of:
class AuditorsPage(webapp2.RequestHandler, myPage.outputHtml):
Try:
class AuditorsPage(webapp2.RequestHandler, myPage):

Related

How to make Class instance in main.py available in another file?

I am having difficulty where I need to make the information available in a class that is instantiated in main.py in another file. The best way to describe what I am trying to do can be seen in the flow diagram below:
The issue as you can imagine is with circular dependency. Is there a way to create an interface between schema.py and main.py so that I can pass class information?
Thank you for your time and any help you can offer!
EDIT: Added code for reference
ws_transport.py
from autobahn.twisted.websocket import (
WebSocketServerProtocol,
WebSocketServerFactory,
)
from schema import schema
class WsProtocol(WebSocketServerProtocol):
def __init__(self):
# Code here
def onConnect(self, request):
# Code here
def onMessage(self, payload, isBinary):
# Code here
class WsProtocolFactory(WebSocketServerFactory):
def __init__(self):
super(WsProtocolFactory, self).__init__()
self.connection_subscriptions = defaultdict(set)
# Code here
def check_events_db(self):
# Code here
def check_audit_log_db(self):
# Code here
web_transport.py
import sys, os
import json
from twisted.web.resource import Resource
from twisted.web.server import Site, http
from schema import schema
class HttpResource(Resource):
isLeaf = True
def render_OPTIONS(self, request):
# Code here
def render_GET(self, request):
# Code here
def render_POST(self, request):
# Code here
class LoginResource(Resource):
isLeaf = True
def render_OPTIONS(self, request):
# Code here
def render_GET(self, request):
# Code here
def render_POST(self, request):
# Code here
class RefreshResource(Resource):
isLeaf = True
def render_OPTIONS(self, request):
# Code here
def render_GET(self, request):
# Code here
def render_POST(self, request):
# Code here
class HttpFactory(Site):
def __init__(self, resource):
# Code here
schema.py
#!/usr/bin/python
import graphene
import json
import sys, os
from main import factory
class Query(graphene.ObjectType):
# Code here
class Mutation(graphene.ObjectType):
# Code here
class Subscription(graphene.ObjectType):
# Code here
schema = graphene.Schema(query=Query, mutation=Mutation, subscription=Subscription)
main.py
import sys
from twisted.internet import reactor
from twisted.web.resource import Resource
from autobahn.twisted.resource import WebSocketResource
from ws_transport import WsProtocol, WsProtocolFactory
from web_transport import HttpResource, LoginResource, RefreshResource, HttpFactory
if __name__ == '__main__':
factory = WsProtocolFactory()
factory.protocol = WsProtocol
ws_resource = WebSocketResource(factory)
root = Resource()
root.putChild("", HttpResource())
root.putChild("login", LoginResource())
root.putChild("refresh", RefreshResource())
root.putChild(b"ws", ws_resource)
site = HttpFactory(root)
reactor.listenTCP(8000, site)
reactor.run()
Cheers,
Brian
I know this is not necessarily the answer you need. But I ran into the same problem and for me it meant that I have structured the project wrong. Meaning main.py or schema.py do things they are not meant to do. Of course you made the project so you get to decide what does what but what I mean is that maybe you should abstract even more. Since I do not quite understand what you want to do with the code since I do not know the libraries.
A simple hacky sort of thing is to just create another file called maybe run.py that then imports both files and dependency-injects main into schema or the other way around.
Another not-so-nice solution is to create an init() function that then imports the other file after the rest has been initialized and therefore insures that the import is only executed once.
But what I did was to check conceptually why main needed to import (in your case) schema and then ask myself is this really what main.py is supposed to do. If e.g. your main needs to provide templates to all other modules then why not create a templates.py or modules.py? Or what in my case was better is to create a bus system. This could allow modules to share only the data needed when needed and exposes a general api. But of course this would only make sense if you only share information.
In conclusion: Usually when the application is well designed you never run into circular imports. When you do it is a sign that you should rethink about how you structure your program.
For Python functions you could do something like this.
def functionInSchema(objectParameter):
# Add in code
This will give you access to the object from the main.py file.
To access attributes/methods use the parameter name, a dot and the attribute/function name.
def functionInSchema(objectParameter):
attribute = objectParameter.attribute
objectParameter.method()
Then in main.py you pass the Class instance (Object) as a parameter.
objectInMain = Class(param1, param2)
functionInSchema(objectInMain)
If you need access to the class in schema.py you can use the following import.
from main.py import ClassName
I hope this helped you! Good luck!

Contextually injecting static class properties in Python

I'm writing a website generator with various classes that represent the content in the webpages such as Page, NewsPost, Tag, Category etc.
I'd like to be able to construct these objects plainly, and I don't have a problem with that.
However, I'd also like to construct these objects within a certain context - say, the context of a website with a particular root URL. Let's say I put this context into an instance of a class ContentManager. This is the code I ultimately hope to end up with:
page = Page(title='Test Page', content='hello world!')
assert page.cm == None
cm = ContentManager(root_url='//localhost')
page = cm.Page(title='Test Page', content='hello world!')
assert page.cm == cm
I can easily manage this if page.cm is a per-instance property set in __init__, but I need to call class methods on cm.Page which need access to the cm object, so it has to be a static property.
If I just set it as a static property on the Page class, it would end up affecting other ContentManagers pages as well, which is not desirable.
How would I achieve this? Metaclasses? Or some sort of class factory function?
One solution could be creating a subclass of Page for every ContentManage instance:
class Page:
cm = None
def __init__(self, title, content):
self.title = title
self.content = content
class ContentManager:
def __init__(self, root_url):
class PerContentManagerPage(Page):
cm = self
self.Page = PerContentManagerPage
page0 = Page(title='Test Page', content='hello world!')
cm = ContentManager(root_url='//localhost')
page = cm.Page(title='Test Page', content='hello world!')
cm2 = ContentManager(root_url='//localhost')
page2 = cm2.Page(title='Test Page 2', content='hello world!')
assert page0.cm is None
assert page.cm == cm
assert page2.cm == cm2
In python a class is also an object (an instance of its metaclass). This solution creates a new subclass of Page every time you instantiate ContentManager. This means that the cm.Page class isn't the same as the cm2.Page class but both are the subclasses of Page. This is why it's possible that cm.Page.cm and cm2.Page.cm have different values, because these are two separate classes (or class objects).
Note: Although in python this could be solved by creating subclass objects dynamically, problems usually have better solutions. Creating classes/subclasses dynamically is a warning sign (HACK).
I'm still convinced that you shouldn't create a page subclass for each content manager instance. Instead I would simply use instances of the global ContentManager and Page classes by connecting them with references to each other in a suitable way and putting the data and the code into instance attributes/methods.
Setting everything else aside, you'll just need to dynamically construct a class to tie to each instance of ContentManager; we can do this using the built-in type function, which can either, with one argument, give us the type of an object, or, with three arguments (class name, base classes, and class dictionary) construct a new class.
Here's a sample of how that might look in your situation:
class Page(object):
# This is just a default value if we construct a Page
# outside the context of a ContentManager
cm = None
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
#classmethod
def do_class_thing(cls):
return cls.cm
class ContentManager(object):
def __init__(self, root_url):
self.url = root_url
"""
This is where the magic happens. We're telling type() to
construct a class, with the class name ContentManagerPage,
have it inherit from the above explicitly-declared Page
class, and then overriding its __dict__ such that the class
cm variable is set to be the ContentManager we're
constructing it from.
"""
self.Page = type(str('ContentManagerPage'), (Page,), {'cm': self})
Once you've got all this set up, it's simple enough to do exactly what you're trying to do, with cm as a class variable.

Unable to patch class instantiated by the tested class using unittest

I am trying to patch a class that is instantiated by the class I am trying to test, but it doesn't work. I have read the various docs but still haven't found what I am doing wrong. Here is the code snippet:
In tests/Test.py:
from module.ClassToTest import ClassToTest
class Test(object):
#mock.patch('module.ClassToPatch.ClassToPatch', autospec = False)
def setUp(self, my_class_mock):
self.instance = my_class_mock.return_value
self.instance.my_method.return_value = "def"
self.class_to_test = ClassToTest()
def test(self):
val = self.class_to_test.instance.my_method() #Returns 'abc' instead of 'def'
self.assertEqual(val, 'def')
In module/ClassToPatch.py:
class ClassToPatch(object):
def __init__(self):
pass
def my_method(self):
return "abc"
In module/ClassToTest.py:
from module.ClassToPatch import ClassToPatch
class ClassToTest(object):
def __init__:
# Still instantiates the concrete class instead of the mock
self.instance = ClassToPatch()
I know in this case I could easily inject the dependency, but this is just an example. Also, we use a single class per file policy, with the file named like the class, hence the weird import naming.
As norbert mentions, the fix is to change the mock line from
#mock.patch('module.ClassToPatch.ClassToPatch', autospec = False)
to
#mock.patch('module.ClassToTest.ClassToPatch', autospec = False)
According to the docs:
The patch() decorator / context manager makes it easy to mock classes or objects in a module under test. The object you specify will be replaced with a mock (or other object) during the test and restored when the test ends.
You are testing the ClassToTest module, not the ClassToPatch module.

How to use the 'self' param inside Spyne server methods

I've seen in many Spyne examples that all the methods don't have the typical self parameter; there aren't examples of Spyne using the self parameter, nor cls. They use a ctx parameter, but ctx doesn't refer to the instance nor to the class (and I need to maintain some state).
Is it possible to use it? Or are the classes not instantiated, and used as static classes?
I was trying to do something similar to:
# -*- coding: utf-8 -*-
from __future__ import (
absolute_import,
unicode_literals,
print_function,
division
)
from spyne.decorator import rpc
from spyne.service import ServiceBase
from spyne.model.primitive import String
class RadianteRPC(ServiceBase):
def __init__(self, name):
self._name = name
#rpc(_returns=String)
def whoami(self):
"""
Dummy test method.
"""
return "Hello I am " + self._name + "!"
The problem with this piece of code is that RadianteRPC never seems to be instantiated as an object by Spyne, but used as a static class.
Solution 1:
As it stands, Spyne doesn't instantiate any object. Then, if we need to store some state, we can do it through class properties.
Since we can't access to the cls parameter in our methods, we need to refer the class by its name, so we can do something like:
class RadianteRPC(ServiceBase):
_name = "Example"
#rpc(_returns=String)
def whoami(ctx): # ctx is the 'context' parameter used by Spyne
"""
Dummy test method.
"""
return "Hello I am " + RadianteRPC._name + "!"
Solution 2 (found in Spyne mailing lists) :
In many cases, it's possible that we can't directly refer to the class name, so we have another alternative: find the class through the ctx parameter.
class RadianteRPC(ServiceBase):
_name = "Example"
#rpc(_returns=String)
def whoami(ctx): # ctx is the 'context' parameter used by Spyne
"""
Dummy test method.
"""
return "Hello I am " + ctx.descriptor.service_class._name + "!"
What I did is to subclass the Application class, and then access the application object through ctx.app.
from spyne.protocol.soap.soap11 import Soap11
from spyne.server.wsgi import WsgiApplication
from spyne import Application, rpc, ServiceBase, Unicode, Boolean
class MyApplication(Application):
def __init__(self, *args, **kargs):
Application.__init__(self, *args, **kargs)
assert not hasattr(self, 'session')
self.session = 1
def increment_session(self):
self.session += 1
def get_session(self):
return self.session
class Service(ServiceBase):
#rpc(_returns=Integer)
def increment_session(ctx):
s = ctx.app.get_session()
self.increment_session()
return s
application = MyApplication([MatlabAdapterService],
'spyne.soap',
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11())
wsgi_application = WsgiApplication(application)
...
I guess there should be a "cleaner" way - not requiring subclassing of the Application class - by subclassing the Context, but this should allow you to work dynamically.
To come back to your question, you also have the opportunity to access your service, since this is defined in the Application.services attribute.

Django - Extending an app's model to override its manager

Using the django-tagging app as an example, I would like to change the manager of the Tag model so that I can replace it with an extended one:
# mytagging/models.py
from django.db import models
from tagging.models import TagManager, Tag
class MyTagManager(TagManager):
def update_tags(self, obj, tag_names):
# My actions
return super(MyTagManager, self).update_tags(obj, tag_names)
def add_tag(self, obj, tag_name):
# My actions
return super(MyTagManager, self).add_tag(obj, tag_name)
Tag.objects = MyTagManager
Now, Tag.objects = MyTagManager doesn't work, nor did I expect it to, but it illustrates what I would like to accomplish. I could very well create class MyTag(Tag) and set the manager that way, but then it would seem that I would also have to extend every other class that uses Tag and change it to MyTag.
I tried Tag.objects = MyTagManager() to initialize the class, but I get the error 'NoneType' object has no attribute '_meta' from a query object, it appears.
The above code renders this error when calling Tag.objects.update_tags(kwargs['instance'], tags) from admin page:
unbound method update_tags() must be called with MyTagManager instance
as first argument (got LibraryFile instance instead)
The LibraryFile model is the one I'm attempting to tag, and should therefore be the second argument instead of first (self being first).
Use proxy model with the different manager:
class MyTag(Tag):
objects = MyTagManager()
class Meta:
proxy = True
Using Secator's suggestion as part of the solution, I ended up just monkey-patching the Tag model itself where needed instead of the Tag.objects.
This being the final code:
from tagging.models import TagManager, Tag
import tagging
class MyTagManager(TagManager):
def update_tags(self, obj, tag_names):
# My actions
return super(MyTagManager, self).update_tags(obj, tag_names)
def add_tag(self, obj, tag_name):
# My actions
return super(MyTagManager, self).add_tag(obj, tag_name)
class MyTag(Tag):
objects = MyTagManager()
class Meta:
proxy = True
tagging.models.Tag = MyTag
tagging.fields.Tag = MyTag

Categories