Decorator Design Pattern in Python - python

I am creating a Flask Website and i want to display different logout links based your current page i.e
If we’re on the home page and logged in, have this link be wrapped in h2 tags
If we’re on a different page and logged in, have this link be wrapped in underline tags
If we’re logged in, have this link wrapped in strong tags
So far i have tried upto here.
class HtmlLinks():
html =""
def set_html(self, html):
self.html = html
def get_html(self):
return self.html
def render(self):
print(self.html)
class LogoutLink(HtmlLinks):
def __init__(self):
self.html = "Logout"
class LogoutLinkH2Decorator(HtmlLinks):
def __init__(self, logout_link):
self.logout_link = logout_link
self.set_html("<h2> {0} </h2>").format(self.logout_link.get_html())
def call(self, name, args):
self.logout_link.name(args[0])
class LogoutLinkUnderlineDecorator(HtmlLinks):
def __init__(self, logout_link):
self.logout_link = logout_link
self.set_html("<u> {0} </u>").format(self.logout_link.get_html())
def call(self, name, args):
self.logout_link.name(args[0])
class LogoutLinkStrongDecorator(HtmlLinks):
def __init__(self, logout_link):
self.logout_link = logout_link
self.set_html("<strong> {0} </strong>").format(self.logout_link.get_html())
def call(self, name, args):
self.logout_link.name(args[0])
logout_link = LogoutLink()
is_logged_in = 0
in_home_page = 0
if is_logged_in:
logout_link = LogoutLinkStrongDecorator(logout_link)
if in_home_page:
logout_link = LogoutLinkH2Decorator(logout_link)
else:
logout_link = LogoutLinkUnderlineDecorator(logout_link)
logout_link.render()
I am getting Attribute error
AttributeError: 'NoneType' object has no attribute 'format'
What wrong i am doing and how to rectify it. Please Help.

So you have a few lines that looks like this:
self.set_html("<h2> {0} </h2>").format(self.logout_link.get_html())
You probably want them to look like:
self.set_html("<h2> {0} </h2>".format(self.logout_link.get_html()))

set_html returns nothing, but you try to call for format method on its returned value.
self.set_html("<strong> {0} </strong>").format(self.logout_link.get_html())

Related

How to extend PageLinkHandler to accept an anchor on a Page

I want to extend Wagtail's PageLinkHandler to combine an internal PageChooser link with an AnchorLink, so the template output is <a href="/mypage#myanchor".
class AnchorPageLinkHandler(PageLinkHandler):
identifier = 'page_anchor'
#staticmethod
def get_model():
return Page
#classmethod
def get_instance(cls, attrs):
return super().get_instance(attrs).specific
#classmethod
def expand_db_attributes(cls, attrs):
try:
page = cls.get_instance(attrs)
hash_id = attrs["hash_id"]
return '<a href="{}#{}">'.format(escape(
page.localized.specific.url), hash_id)
except Page.DoesNotExist:
return "<a>"
#hooks.register('register_rich_text_features')
def register_link_handler(features):
features.register_link_type(AnchorPageLinkHandler)
I have got this far, but have 2 problems: i) page_anchor does not show up in the link editor widget, how do I enable this? ii) how do I add the hash_id text input to the widget when I do enable it?

How to solve "Unresolved attribute reference for class"

I have been working on a small project which is a web-crawler template. Im having an issue in pycharm where I am getting a warning Unresolved attribute reference 'domain' for class 'Scraper'
from abc import abstractmethod
import requests
import tldextract
class Scraper:
scrapers = {}
def __init_subclass__(scraper_class):
Scraper.scrapers[scraper_class.domain] = scraper_class # Unresolved attribute reference 'domain' for class 'Scraper'
#classmethod
def for_url(cls, url):
k = tldextract.extract(url)
# Returns -> <scraper.SydsvenskanScraper object at 0x000001E94F135850> & Scraped BBC News<!DOCTYPE html><html Which type annotiation?
return cls.scrapers[k.registered_domain](url)
#abstractmethod
def scrape(self):
pass
class BBCScraper(Scraper):
domain = 'bbc.co.uk'
def __init__(self, url):
self.url = url
def scrape(self):
rep = requests.Response = requests.get(self.url)
return "Scraped BBC News" + rep.text[:20] # ALL HTML CONTENT
class SydsvenskanScraper(Scraper):
domain = 'sydsvenskan.se'
def __init__(self, url):
self.url = url
def scrape(self):
rep = requests.Response = requests.get(self.url)
return "Scraped Sydsvenskan News" + rep.text[:20] # ALL HTML CONTENT
if __name__ == "__main__":
URLS = ['https://www.sydsvenskan.se/', 'https://www.bbc.co.uk/']
for urls in URLS:
get_product = Scraper.for_url(urls)
r = get_product.scrape()
print(r)
Of course I could ignore it as it is working but I do not like to ignore a warning as I believe pycharm is smart and should solve the warning rather than ignoring it and I wonder what is the reason of it warns me regarding that?
There are a few different levels on how you can remove this warning:
Assign a default value:
class Scraper:
scrapers = {}
domain = None # Or a sensible value of one exists
You can in additon or alternatly annotate the type.
from typing import ClassVar
class Scraper:
scrapers: ClassVar[dict[str, 'Scraper']] = {}
domain: ClassVar[str]
Note that ClassVar is required because otherwise it is assume that they are instance attributes.
To ignore it, put
# noinspection PyUnresolvedReferences
on the line above the line causing the warning.
Just tell yrou Scraper class that this attribut exists
class Scraper:
scrapers = {}
domain: str
def __init_subclass__(scraper_class):
Scraper.scrapers[scraper_class.domain] = scraper_class

Initiate property inside python class

I'm trying to initiate a property inside a class but it doesn't seem to work.
The class should use another class. this is the code I've made:
Book.py:
from Paragraph import Paragraph as Paragraph
'''definitions'''
class Book:
def __init__(self, book):
self._paragraphs = book
'''paragraphs property'''
#property
def paragraphs(self):
return self._paragraphs
#paragraphs.setter
def paragraphs(self, book):
paragraph_0 = Paragraph(book[0x0:0x100])
paragraph_1 = Paragraph(book[0x200:0x300])
paragraph_2 = Paragraph(book[0x300:0x400])
paragraph_3 = Paragraph(book[0x400:0x500])
self._paragraphs = [paragraph_0, paragraph_1, paragraph_2, paragraph_3]
Paragraph.py:
'''definitions'''
class Paragraph:
def __init__(self, paragraph):
self._paragraph = paragraph
self._first_section = paragraph[0x0:0x10]
self._second_section = paragraph[0x10:0x100]
'''paragraph property'''
#property
def paragraph(self):
return self._paragraph
#paragraph.setter
def paragraph(self, paragraph):
self._paragraph = paragraph
'''first_section property'''
#property
def first_section(self):
return self._first_section
#first_section.setter
def first_section(self, first_section):
self._first_section = first_section
'''second_section property'''
#property
def second_section(self):
return self._second_section
#second_section.setter
def second_section(self, second_section):
self._second_section = second_section
Then I create a Book instance:
first_book = Book(some_hex_list)
And I expect to have 4 paragraphs in first_book._paragraphs and each paragraph should have a first and a second section property (as in the Paragraph class). Instead first_book._paragraphs just contains the original some_hex_list data.
I'd like to create a Book instance and initiate it so in the end I should get something like this:
first_book
-->_paragraphs
----->paragraph_0
---------->_first_section
---------->_second_section
----->paragraph_1
---------->_first_section
---------->_second_section
----->paragraph_2
---------->_first_section
---------->_second_section
----->paragraph_3
---------->_first_section
---------->_second_section
Thanks!

Python Script Connecting To MySQL Database - Code ran as procedural script, fails after OOP refactor

I have a script that can run a number of different reports, based on what input it receives via the command line. All reports read from a database and return the results as a Pandas Dataframe object.
Here is the super-class, (omitting a large number of property getter and setter functions):
import mysql.connector
import pandas as p
import config
class Report(object):
_connection = None
_cursor = None
def __init__(self):
self._user = config.user
self._password = config.password
self._host = config.host
self._database = config.database
self._port = config.port
self._body_text = "Hello,\n\nPlease find attached these reports:\n\n"
self._connection = mysql.connector.connect(user=self._user, password=self._password, db=self._database,
host=self._host, port=self._port)
self._cursor = self._connection.cursor()
#property
def user(self):
return self._user
#user.setter
def user(self, value):
self._user = value
. . .
#property
def body_text(self):
return self._body_text
#body_text.setter
def body_text(self, value):
self._body_text = value
def append_body_text(self, value):
self._body_text += value
def get_data(self, server_cursor, query, columns):
server_cursor.execute(self, query)
results = server_cursor.fetchall()
data = p.DataFrame(data=results, columns=[columns])
return data
def get_today(self):
return self.today
def close(self):
self._connection_web.close()
self._connection_raw.close()
#staticmethod
def insert_variables_into_sql_statement(query, external_data):
final_query = query % external_data
return final_query
#staticmethod
def create_string_from_column(serial):
created_list = serial.tolist()
string = ', '.join(map(str, created_list))
return string
#staticmethod
def write_to_csv(data_frame, file_name):
data_frame.to_csv(config.baseDirectory + file_name, sep=',', index=False)
def generate_report(self):
data = self.get_data(self._cursor_web, self._query, self._columns)
self.write_to_csv(data, self._filename)
self.close()
Here is how my subclasses are structured:
class ExampleReport(Report):
def __init__(self):
Report.__init__(self)
self._query = """
SELECT
u.first_name AS 'First Name',
u.last_name AS 'Last Name'
FROM users AS u
"""
self._columns = "'FirstName', 'LastName'"
self._filename = "example_report.csv"
self.append_body_text("* All users")
In my main method I call the method like this:
report = Reports.ExampleReport()
report.generate_report()
When I do this, I get the following error:
AttributeError: 'ExampleReport' object has no attribute 'encode'
My database connections worked without a problem when it was terribly constructed procedural code (a working version is currently in production). It has broken now that I've tried to make it object oriented. Does anyone have any idea what I've done wrong?
EDIT: SOLVED MY OWN PROBLEM! In the get_data function in the super-class, the second line contained an erroneous self argument passed into server_cursor.execute(query) line. Once it was taken out, the error goes away.
SOLVED MY OWN PROBLEM! In the get_data function in the super-class, the second line contained an erroneous self argument passed into server_cursor.execute(query) line. Once it was taken out, the error goes away.

Is this the correct way of doing/handling url rewrite in App Engine/ Python?

I'm not sure if this is effective or not. It works, but sometimes i feel...weird about it. Can you please tell me if this is a good way or not?
I threw the code on pastebin, because i think it's a bit too much to put here: http://pastebin.com/662TiQLq
EDIT
I edited the title to make it more objective.
I'm just guessing that the questioner is asking about creating a dictionary of functions in the __ init __ function of the handlers, and then using this dict in the "get" function to look up specific functions. If this is the question, then IMHO a clearer approach would be to set up separate handlers for each different function. For example
class QuotesView(webapp.RequestHandler):
"""Super class for quotes that can accommodate common functionality"""
pass
class QuotesViewSingle(QuotesView):
def get(self):
...
class QuotesViewRandom(QuotesView):
def get(self):
...
class QuotesViewAll(QuotesView):
def get(self):
...
def main():
application = webapp.WSGIApplication([('/quote/new',NewQuote),
(r'/quotes/single',QuotesViewSingle),
(r'/quotes/all',QuotesViewAll),
(r'/quotes/random',QuotesViewRandom),
...
('/', MainHandler)],
debug=True)
BTW. A lot of people use the regex in the WSGIApplication calls to parse out arguments for the get functions. There's nothing particularly wrong with it. I'm not a big fan of that feature, and prefer to parse the arguments in the get functions. But that's just me.
For completeness here's the original code:
class Quote(db.Model):
author = db.StringProperty()
string = db.StringProperty()
class MainHandler(webapp.RequestHandler):
def get(self):
user = users.get_current_user()
quotes = Quote.all()
path = os.path.join(os.path.dirname(__file__),'quotery.html')
template_values = {'quotes':quotes,'user':user,'login_url':users.create_login_url('/')}
self.response.out.write(template.render(path, template_values))
class QuoteHandler(webapp.RequestHandler):
def __init__(self):
self.actions = {'fetch':self.fetch, 'random':self.fetch_random}
#Memcache the number of quotes in the datastore, to minimize datastore calls
self.quote_count = memcache.get('quote_count')
if not self.quote_count:
self.quote_count = self.cache_quote_count()
def cache_quote_count(self):
count = Quote.all().count()
memcache.add(key='quote_count', value=count, time=3600)
return count
def get(self, key):
if key in self.actions:
action = self.actions[key]
action()
def fetch(self):
for quote in Quote.all():
print 'Quote!'
print 'Author: ',quote.author
print 'String: ',quote.string
print
def fetch_random(self):
max_offset = self.quote_count-1
random_offset = random.randint(0,max_offset)
'''self.response.out.write(max_offset)
self.response.out.write('\n<br/>')
self.response.out.write(random_offset)'''
try:
query = db.GqlQuery("SELECT * FROM Quote")
quotes = query.fetch(1,random_offset)
return quotes
'''for quote in quotes:
self.response.out.write(quote.author)
self.response.out.write('\n')
self.response.out.write(quote.string)'''
except BaseException:
raise
class NewQuote(webapp.RequestHandler):
def post(self):
author = self.request.get('quote_author')
string = self.request.get('quote_string')
if not author or not string:
return False
quote = Quote()
quote.author = author
quote.string = string
quote.put()
QuoteHandler().cache_quote_count()
self.redirect("/")
#return True
class QuotesView(webapp.RequestHandler):
def __init__(self):
self.actions = {'all':self.view_all,'random':self.view_random,'get':self.view_single}
def get(self, key):
if not key or key not in self.actions:
self.view_all()
if key in self.actions:
action = self.actions[key]
action()
def view_all(self):
print 'view all'
def view_random(self):
quotes = QuoteHandler().fetch_random()
template_data = {}
for quote in quotes:
template_data['quote'] = quote
template_path = os.path.join(os.path.dirname(__file__),'base_view.html')
self.response.out.write(template.render(template_path, template_data))
def view_single(self):
print 'view single'
def main():
application = webapp.WSGIApplication([('/quote/new',NewQuote),(r'/quotes/(.*)',QuotesView),(r'/quote/(.*)',QuoteHandler),('/', MainHandler)],
debug=True)
util.run_wsgi_app(application)

Categories