Cannot access my nested function which is inside the class - python

So Inside a class I have a function and inside that function I have a series of functions. But I cannot call the nested function when I create the object.user.searchBooks.AuthorBooks() (where user is an instance of the object) does not work. I don't know whether I am doing this wrong. Is there another way of accessing the function
class User():
def __init__(self):
self.ID = None
self.Firstname = None
self.Surname = None
self.username = None
self.email = None
def searchBook(self):
'''Searching Books with given query'''
def AuthorBooks(self, aFirstname, aLastname, Type):
"""Query based on Author Searches - returns books with particular author """
if Type == 'All':
query = ("""
SELECT * FROM tblBooks;
SELECT * FROM tblAuthor;
SELECT cb.Name, cb.Genre, cb.Year_Published, ca.Firstname, ca.Surname
FROM tblBooks as cb
INNER JOIN tblAuthor ca on cb.AuthorID = ca.AuthorID
WHERE (UPPER(ca.Firstname)) = (UPPER(?)) or (UPPER(ca.Surname) = UPPER(?));""")
elif Type == 'Loaned Books':
query = ("""
SELECT tblBooks.Name, tblAuthor.Firstname, tblAuthor.Surname, tblBooks.BookID, tblLoans.Expiry_Date
FROM tblLoans
INNER JOIN (tblBooks INNER JOIN tblAuthor ON tblBooks.AuthorID = tblAuthor.AuthorID) ON tblBooks.BookID = tblLoans.BookID
WHERE ((UPPER(tblAuthor.Firstname) = UPPER(?)) or(UPPER(tblAuthor.Surname) = UPPER(?)) AND tblLoans.Date_of_Return IS NULL;
""")
cursor.execute(query, [(aFirstname), (aLastname)])
return cursor.fetchall()

Since the function AuthorBooks() is defined inside of searchBook(), this makes it inaccessible from outside of the searchBook() function. (All variables and functions defined inside of a function are generally only accessible within that function in which they were defined)
To make your code work, you could define AuthorBooks() as a separate function and access it directly using user.AuthorBooks().

Related

Python - Problem returning True/False to class properties from class method

I have a class as below which within the __init__ method I am trying to assign a True/False value to a class property using a class method.
class Sensor:
def __init__(self, json_data):
self.sensor_eui = json_data['end_device_ids']['dev_eui']
self.reading1 = json_data['uplink_message']['decoded_payload']['temperature']
self.reading2 = json_data['uplink_message']['decoded_payload']['humidity']
self.tolerance_exceeded = self.tolerance_check
def tolerance_check(self):
sql = f"SELECT DefaultLowerLimit, DefaultUpperLimit FROM [dbo].[IoT_Sensors] WHERE
DeviceID = '{self.sensor_eui}'"
results = exec_sql(sql)
if (self.reading1 > int(results[0])) and (self.reading1 < int(results[1])):
return False
return True
The issue is, when trying to troubleshoot this and logging the objects to the console, instead of returning True or False as the assigned value of 'tolerance_exceeded' it returns the method and object:
logging.info(f'Tolerance Exceeded: {sensor.tolerance_exceeded}')
logs in the console as below:
[2022-10-26T12:08:08.025Z] Tolerance Exceeded: <bound method Sensor.tolerance_check of <__app__.IoT_Data-Handler.classes.Sensor object at 0x000001C834D45BE0>>
So what is going on here? I have not been coding long, but when I have done something similar in the past (assigning a string value from an API), it worked fine. This is for an Azure Function, but I cannot see how that would impact what I am trying to achieve.
Any help would be appreciated.
The issue in your code is that instead of calling the function you assign it. In order to call the function you have to add the parenthesis.
class Sensor:
def __init__(self, json_data):
self.sensor_eui = json_data['end_device_ids']['dev_eui']
self.reading1 = json_data['uplink_message']['decoded_payload']['temperature']
self.reading2 = json_data['uplink_message']['decoded_payload']['humidity']
# Calling tolerance_check and assigning return value to tolerance_exceeded
self.tolerance_exceeded = self.tolerance_check()

How to mimic multiple returns for reused api call using pytest

I'm trying to test a function that looks like this that calls an SDK.
FUNCTION TO BE TESTED
def create_folder_if_not_exists(
sdk: looker_sdk,
folder_name: str,
parent_id: str) -> dict:
folder = sdk.search_folders(name=folder_name)
if folder or parent_id == '1':
folder = folder[0]
raise Exception(
f"Folder {folder_name} already exists or is a reserved folder")
else:
parent_id = sdk.search_folders(name=parent_id)[0].id
logger.info(f'Creating folder "{folder_name}"')
folder = sdk.create_folder(
body=models.CreateFolder(
name=folder_name,
parent_id=parent_id
)
)
return folder
The test that i'm writing has to be able to mock the search_folders endpoint in two different ways, first passing no value, then passing a value for the else condition, which should return an id field. The library i've been using is pytest and trying to use side effects but i'm fairly new to testing and not sure how to do this. My current code is as follows
Creating a few mock objects that mimic the api structure
class MockSDK():
def search_folders(self):
pass
def create_folder(self):
pass
class MockSearchFolder():
def __init__(self, parent_id, name):
self.parent_id = parent_id
self.name = name
class MockCreateFolder():
def __init__(self, parent_id, folder_name):
self.parent_id = parent_id
self.folder_name = folder_name
Defining a side_effect with two values where I return the side effects
def mock_search_folder(**kwargs):
if kwargs.get('folder_name') == 'googn':
return None
else:
return MockSearchFolder(parent_id='frankie fish', name='nice guy eddie')
def test_create_folder_if_not_exists():
# Tests the creation of a folder if it doesn't exist or have a parent id of 1
sdk = MockSDK()
tt = MockSearchFolder(name='dd', parent_id='dddd')
with unittest.mock.patch.object(sdk, 'search_folders', side_effect=mock_search_folder) as sf:
test = fc.create_folder_if_not_exists(
sdk=sdk, folder_name='googn', parent_folder_name='4')
assert test == 'frank'
I don't think i'm passing return values in the with statement, but perhaps i'm wrong.
Would anyone be able to take a glance at this and tell me what i'm doing wrong ?
Thank you!
Update, i think i've been overthinking this, defining the side_effects as an array did the trick, enabling the first call to be returned as None and the second returned as my mock object.
def test_create_folder_if_not_exists_xyz(mocker):
# Tests the creation of a folder if it doesn't exist or have a parent id of 1
sdk = MockSDK()
sf_data = None
sf_data1 = [MockSearchFolder(name='frank', parent_id=4, id=55)]
mocker.patch.object(sdk, 'search_folders', side_effect=[sf_data, sf_data1])
test = fc.create_folder_if_not_exists(
sdk=sdk, folder_name='googn', parent_folder_name='4')
assert test == 'frank'

field back to zero after save

I have this class in a module1:
class A(models.Model):
_name="a"
b_id = field.Many2one("b")
tax_old = fields.Float()
tax_value = fields.Float(string="Tax", related = 'b_id.tax_value', store=True)
all_taxes = fields.Float(_compute='compute_all')
#api.depends('tax_value')
def compute_all(self):
self.all_taxes = self.tax_value + self.tax_old
self.update()
In module2 I have this class:
class B(models.Model):
_name="b"
a_ids = fields.One2many("a","b_id")
tax_value = fields.Float(string="Tax")
Now in A view when I change b_id value, tax_value works fine and compute_all works fine, but when I save this record, all_taxes doesn't take tax_value field, only tax_old. And when I open the record form view again and manually write a value in tax_value, it works totally fine.
It should be enough to use b_id on your compute method, because it's related:
#api.multi
#api.depends('b_id')
def compute_all(self):
for record in self:
record.all_taxes = record.b_id.tax_value + record.tax_old
The compute method can be called with a multi record recordset. So use a for loop inside it. And you don't have to do an update() at the end.
You can try it
#api.one
#api.depends('b_id', 'b_id.tax_value')
def compute_all(self):
self.all_taxes = self.tax_value + self.tax_old
Two things:
It ist compute not _compute and you don't need to use self.update().
Try this instead:
# You'll need this
from django.db.models import F
#api.depends('tax_value')
def compute_all(self):
self.update(all_taxes=F('tax_value') + F('tax_old'))
You're missing the self. What you've done is defined a local variable called all_taxes, not the instance variable.. which is what you're after

How to create object from another object in Python unit test

I'm having trouble understanding unit testing in Python. I have an object, retailer, which creates another object, deal. deal refers to an attribute created in retailer, so I'm passing it a reference:
class deal():
def __init__(self, deal_container, parent):
The deal_container attribute also comes from retailer, which calls its own methods to create it. So how do I create everything I need to easily make a deal object?
Do I have to create an instance of retailer in my unit test and then call the method in that object that creates deal?
Can I use FactoryBoy to create an instance of retailer and how do I include the method that creates deal in that object?
What's the best way to approach this?
Here's the unit test. I'm setting up the soup_obj I need to give deal:
class TestExtractString(TestCase):
fixtures = ['deals_test_data.json']
def setUp(self):
with open('/home/danny/PycharmProjects/askarby/deals/tests/BestBuyTest.html', 'r') as myfile:
text = myfile.read().replace('\n', '')
self.soup_obj = bs4.BeautifulSoup(text,"html.parser")
self.deal = self.soup_obj.find_all('div',attrs={'class':'list-item'})[0]
def test_extracts_title(self):
z = Retailer.objects.get(pk=1)
s = dealscan.retailer(z)
d = dealscan.deal(self.deal,s)
result = d.extract_string(self.deal,'title')
and here's the relevant bit of the deal class in dealscan. There's a retailer class that creates a deal, but I haven't even written the bit in retailer that creates deal yet. I'm hoping I can mock the bits I need for deal without having to invoke retailer at all, but then how do I deal with the fact that deal references retailer?
class deal():
def __init__(self, deal_container, parent):
'''
Initializes deal object
Precondition: 0 > price
Precondition: 0 > old_price
Precondition: len(currency) = 3
:param deal_container: obj
'''
self.css = self.parent.css
self.deal_container = deal_container
self.parent = parent
self.title = self.extract_string('title')
self.currency = self.parent.currency
self.price = self.extract_price('price')
self.old_price = self.extract_price('old_price')
self.brand = self.extract_string('brand')
self.image = self.extract_image('image')
self.description = self.extract_string('description')
#define amazon category as clearance_url
#define all marketplace deals
def __str__(self):
return self.title
def extract_string(self, element, deal):
'''
:param object deal: deal object to extract title from
:param string element: element to look for in CSS
:return string result: result of string extract from CSS
'''
tag = self.css[element]['tag']
attr = self.css[element]['attr']
name = self.css[element]['name']
result = deal.find(tag, attrs={attr: name})
if result:
if element == 'title':
return result.text
elif element == 'price':
result = self.extract_price(result).text
if result:
return result
elif element == 'image':
result = self.extract_image(result)
return False
The problem is that the deal object is referencing the parent before it sets the self.parent attribute. Use:
self.parent = parent
self.css = self.parent.css
self.deal_container = deal_container
and the AttributeError goes away.
As for the question about whether it's good form to use an object to create another object in a unit test, the answer is that you can use mocks, but it's fine to do it this way. Using a helper method to set up the parent object once in setUp is acceptable and will make the code easier to read, and may improve test performance a little.

why do some functions within a python class require the use of brackets and others dont

Below I have defined a basic class with some functions.
I am not sure of the best way to pass data from one function to be used in another.
My solution was to pass a dataframe as a parameter to the function. I am sure there is a better and more technically correct way so please call it out.
What I am trying to understand is why some functions in the class require the () to be used when calling them and others you can call just using the function name.
Once a ASX object called "market" is initiated The two examples are:
market.all_companies()
Returns: a dataframe
market.valid_industry
returns a series
class YourError( Exception ): pass
class asx(object):
def __init__(self, name):
try:
#initialise
self.name = name
#all companies on asx downloaded from asx website csv
df = pd.read_csv('http://asx.com.au/asx/research/ASXListedCompanies.csv', skiprows=1)
df.columns = ["company","asx_code","industry"]
df["yahoo_code"] = df["asx_code"]+".AX"
self.companies = df
self.industry = self.all_industry(df)
self.valid_stocks = self.valid_stocks(df)
self.valid_industry = self.valid_industry(df)
except:
raise YourError("asx companies CSV not available")
def all_companies(self):
return self.companies
def valid_industry(self,df):
return df["industry"].value_counts()
def all_industry(self,df):
return df["industry"].value_counts()
def valid_stocks(self,df):
return df[(df["industry"]!= "Not Applic") & (df["industry"]!="Class Pend")]
market = asx("asx")
market.all_companies()
market.valid_industry
All functions require () but you're doing some nasty stuff in you __init__ where you replace function with a series.
self.valid_industry = self.valid_industry(df)
this will overwrite the function valid_industry to no longer be a function on the instance created but to be value returned from self.valid_industry(df)
don't use same name for member properties and methods and all will make sense.
For your methods you don't need to pass in df as argument as you have it assigned to self.companies so your
def valid_industry(self,df):
return df["industry"].value_counts()
becomes:
def valid_industry(self):
return self.companies["industry"].value_counts()

Categories