Access variable from a different function from another file - python

I have a file myfunctions.py in directory mypythonlib
from requests_html import HTMLSession
import requests
def champs_info(champname:str, tier_str:int):
url = f"https://auntm.ai/champions/{champname}/tier/{tier_str}"
session = HTMLSession()
r = session.get(url)
r.html.render(sleep=1, keep_page=True, scrolldown=1)
information = r.html.find("div.sc-hiSbYr.XqbgT")
sig = r.html.find('div.sc-fbNXWD.iFMyOV')
tier_access = information[0]
tier = tier_access.text
I want to access the variable tier through another file- test_myfunctions.py
but the thing is I also have to give parameters to the function champs_info so that it could access the url accordingly.
from mypythonlib import myfunctions
def test_champs_info():
return myfunctions.champs_info("abomination",6).tier
But while running this code, I am getting the error-
./tests/test_myfunctions.py::test_champs_info Failed: [undefined]AttributeError: 'NoneType' object has no attribute 'tier'
def test_champs_info():
> return myfunctions.champs_info("abomination",6).tier
E AttributeError: 'NoneType' object has no attribute 'tier'
tests/test_myfunctions.py:3: AttributeError
Any Solution for this and why is this code not able to access the variable?
I wrote myfunctions.champs_info("abomination",6).tier in hope for that it's gonna take the tier variable from the champs_info function while giving it the parameters required all from the myfunctions file :(

You can access the value of a variable in a function by 1) returning the value in the function, 2) use a global variable in the module, or 3) define a class.
If only want to access a single variable local to a function then the function should return that value. A benefit of the class definition is that you may define as many variables as you need to access.
1. Return value
def champs_info(champname:str, tier_str:int):
...
tier = tier_access.text
return tier
2. global
tier = None
def champs_info(champname:str, tier_str:int):
global tier
...
tier = tier_access.text
Global tier vairable is accessed.
from mypythonlib import myfunctions
def test_champs_info():
myfunctions.champs_info("abomination", 6)
return myfunctions.tier
print(test_champs_info())
3. class definition:
class Champ:
def __init__(self):
self.tier = None
def champs_info(self, champname:str, tier_str:int):
...
self.tier = tier_access.text
test_functions.py can call champs_info() in this manner.
from mypythonlib import myfunctions
def test_champs_info():
info = myfunctions.Champ()
info.champs_info("abomination", 6)
return info.tier
print(test_champs_info())

In myfunctions.champs_info() add a return tier
and in the script test_myfunctions.py remove .tier

You just have to return tier from champs_info() function
Just like this:
myfunctions.py
from requests_html import HTMLSession
import requests
def champs_info(champname:str, tier_str:int):
url = f"https://auntm.ai/champions/{champname}/tier/{tier_str}"
session = HTMLSession()
r = session.get(url)
r.html.render(sleep=1, keep_page=True, scrolldown=1)
information = r.html.find("div.sc-hiSbYr.XqbgT")
sig = r.html.find('div.sc-fbNXWD.iFMyOV')
tier_access = information[0]
tier = tier_access.text
return tier # <---- Focus Here
test_myfunctions.py
import myfunctions
print(temp.champs_info("americachavez", 6))
Just it. You're done.

Related

AttributeError: 'str' object has no attribute 'soup'

I am just trying to print my raw text from bs4. However, I cant access the attribute from inside one of my methods. But I can access the attribute just fine from outside the class.
import requests
from bs4 import BeautifulSoup
from pprint import pp
import datetime
import time
stocklist = ["wish","clov","baba","pltr",'mu','nio','sofi','tsla','gme','clne',]
class Stock:
def __init__(self,stocklist,s):
self.address = Stock.AG(stocklist,s)
self.soup = Stock.Soup(stocklist,s)
self.volume = Stock.Volume(stocklist,s)
self.price = Stock.Price(stocklist,s)
def AG(stocklist,s):
stockurl = str(('https://robinhood.com/stocks/'+stocklist[s]))
return(stockurl)
def Soup(stocklist,s):
r = requests.get('https://robinhood.com/stocks/'+stocklist[s])
soup = str(BeautifulSoup(r.content, 'lxml'))
return(soup)
def Volume(stocklist,s):
print(stocklist[s].soup) # does not work
def Price(stocklist,s):
pass
for s in range(len(stocklist)):
stocklist[s] = Stock(stocklist,s)
print(stocklist[s].address)
print(stocklist[s].volume)
print(stocklist[s].soup) #works
So this is because when you are calling the volume function stocklist[s] still refers to the string not to the object. Think about when you are calling the init method and hows it being called before the list is overwritten.
The very quick and dirty solution I came up with which should not be deployed but highlights the problem is to change you init method to this.
def __init__(self,stocklist,s):
self.address = Stock.AG(stocklist,s)
self.soup = Stock.Soup(stocklist,s)
stocklist[s] = self
self.volume = Stock.Volume(stocklist,s)
self.price = Stock.Price(stocklist,s)

Pytest Mock AWS SecurityManager

my project has a file called config.py which has, among others, the following code:
class Secret(Enum):
DATABASE_A = 'name_of_secret_database_A'
DATABASE_A = 'name_of_secret_database_A'
def secret(self):
if self.value:
return get_secret(self.value)
return {}
def get_secret(secret_name):
session = Session()
client = session.client(
service_name='secretsmanager',
region_name='us-east-1',
)
secret_value = client.get_secret_value(SecretId=secret_name)
return loads(secret_value.get('SecretString', "{}"))
I need to somehow mock get_secret in tests with pytest for all enum calls, for example Secret.DATABASE_A.secret ()
You can use monkeypatch to override the behaviour of get_secret(). I have made the get_secret() method a static method of the Secret class, but you can make it part of any module you want and import it as well. Just make sure you change in in the monkeypatch.setattr() call as well.
import pytest
from enum import Enum
class Secret(Enum):
DATABASE_A = 'name_of_secret_database_A'
DATABASE_B = 'name_of_secret_database_B'
def secret(self):
if self.value:
return Secret.get_secret(self.value)
return {}
#staticmethod
def get_secret(secret_name):
session = Session()
client = session.client(
service_name='secretsmanager',
region_name='us-east-1',
)
secret_value = client.get_secret_value(SecretId=secret_name)
return loads(secret_value.get('SecretString', "{}"))
def test_secret_method(monkeypatch):
def get_secret(secret_name):
return "supersecret"
monkeypatch.setattr(Secret, "get_secret", get_secret)
s = Secret.DATABASE_A
assert s.secret() == "supersecret"
This returns into 1 passed test.
What is happening here is, that I created a function get_secret() in my test_secret_method as well, and then overwrite the Secret.get_secret() with that new method. Now, you can use the Secret class in your test_method and be sure what the 'get_secret()' method will return without actually running the original code.

Reference to same instance in unittest (Not creating new instance when an object is called)

Background:
I created a class to extract info from google. This class searches the search term and suggest correction if there is any spelling error with the search term.
class gs:
url_part1 = "https://www.googleapis.com/customsearch/v1?key="
key = "some_key"
cse = 'some_key'
suggestion = 'None'
def __init__(self, search):
self.search = search
self.url = gs.url_part1 + gs.key + gs.cse + self.search
#gs.suggestion = 'None'
def get_suggestion(self):
r = requests.get(url=self.url)
r_json = r.json()
if 'spelling' in r_json.keys():
gs.suggestion = r_json.get('spelling').get('correctedQuery')
return gs.suggestion
For example if I search for paytn (correct name is paytm) then this class suggestion correction for paytn which is paytm
gs_1 = gs(search='paytn')
suggestion_1 = gs_1.get_suggestion()
print(suggestion_1) #suggestion for incorrect name
'paytm'
Instead of searching for paytn if I search for 'paytm' then gs class suggests nothing.
gs_2 = gs(search='paytm')
suggestion_2 = gs_2.get_suggestion()
print(suggestion_2) #no suggestion for correct name
'None'
Now while creating unittest case for gs class I observed a very strange behavior. self.gs_2 is picking up the the values from self.gs_1
and failing the below test.
from search import gs
import unittest
class TestSearch(unittest.TestCase):
def setUp(self):
self.gs_1 = gs(search='paytn')
self.gs_2 = gs(search='paytm')
def test_get_suggestion(self):
self.assertEqual(self.gs_1.get_suggestion(), 'paytm')
self.assertEqual(self.gs_2.get_suggestion(), 'None')
if __name__ == '__main__':
unittest.main()
Upon inspection it is clear that self.gs_2 is acquiring value from first instance of class gs. Please refer to the output below
import search
suggestion 1 in setUp paytm
suggestion 2 in setUp paytm
F
======================================================================
FAIL: test_get_suggestion (test.TestSearch)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\YBL\test.py", line 15, in test_get_suggestion
self.assertEqual(self.gs_2.get_suggestion(), 'None')
AssertionError: 'paytm' != 'None'
- paytm
+ None
----------------------------------------------------------------------
Ran 1 test in 3.028s
FAILED (failures=1)
Can anyone help me to understand
1) whether my understanding is correct or not ?
2) Why it is getting values from gs_1 instead of creating a new instance ?
class gs:
url_part1 = "https://www.googleapis.com/customsearch/v1?key="
key = "some_key"
cse = 'some_key'
def __init__(self, search):
self.search = search
self.url = gs.url_part1 + gs.key + gs.cse + self.search
self.suggestion = 'None'
def get_suggestion(self):
r = requests.get(url=self.url)
r_json = r.json()
if 'spelling' in r_json.keys():
self.suggestion = r_json.get('spelling').get('correctedQuery')
return self.suggestion
You used suggestion in gs (same for the whole class), rather than self.suggestion that is instance-specific. You can see it in a way you accessed the value - gs.suggestion means it belongs to gs, self.suggestion means it belongs to the current instance that was passed to the method. ;)
In your case, 1st pass overwrote the gs.suggestion and the 2nd didn't, so it returned the previous value.

Trouble passing variable as parameter between methods

I've created a script in python using class to log into a website making use of my credentials. When I run my script, I can see that it successfully logs in. What I can't do is find a suitable way to pass res.text being returned within login() method to get_data() method so that I can process it further. I don't wish to try like this return self.get_data(res.text) as it looks very awkward.
The bottom line is: when I run my script, It will automatically logs in like it is doing now. However, it will fetch data when I use this line scraper.get_data() within main function..
This is my try so far:
from lxml.html import fromstring
import requests
class CoffeeGuideBot(object):
login_url = "some url"
def __init__(self,session,username,password):
self.session = session
self.usrname = username
self.password = password
self.login(session,username,password)
def login(self,session,username,password):
session.headers['User-Agent'] = 'Mozilla/5.0'
payload = {
"Login1$UserName": username,
"Login1$Password": password,
"Login1$LoginButton": "Log on"
}
res = session.post(self.login_url,data=payload)
return res.text
def get_data(self,htmlcontent):
root = fromstring(htmlcontent,"lxml")
for iteminfo in root.cssselect("some selector"):
print(iteminfo.text)
if __name__ == '__main__':
session = requests.Session()
scraper = CoffeeGuideBot(session,"username","password")
#scraper.get_data() #This is how i wish to call this
What is the ideal way to pass variable as parameter between methods?
If I understood you requirement correctly, you want to access res.text inside get_data() without passing it as a method argument.
There are 2 options IMO.
Store res as a class instance variable of CoffeeGuideBot, access it in get_data()
def login(self,session,username,password):
<some code>
self.res = session.post(self.login_url,data=payload)
def get_data(self):
root = fromstring(self.res.text,"lxml")
<other code>
Almost same as above, but actually use the return value from login() to store res. In current code, the return statement is unnecessary.
def __init__(self,session,username,password):
<initializations>
self.res = self.login(session,username,password)
def login(self,session,username,password):
<some code>
return session.post(self.login_url,data=payload)
def get_data(self):
root = fromstring(self.res.text,"lxml")
<other code>
from lxml.html import fromstring
import requests
class CoffeeGuideBot(object):
login_url = "some url"
def __init__(self,session,username,password):
self.session = session
self.usrname = username
self.password = password
self._login = self.login(session,username,password)
def login(self,session,username,password):
session.headers['User-Agent'] = 'Mozilla/5.0'
payload = {
"Login1$UserName": username,
"Login1$Password": password,
"Login1$LoginButton": "Log on"
}
res = session.post(self.login_url,data=payload)
return res.text
def get_data(self):
htmlcontent = self._login
root = fromstring(htmlcontent,"lxml")
for iteminfo in root.cssselect("some selector"):
print(iteminfo.text)
if __name__ == '__main__':
session = requests.Session()
scraper = CoffeeGuideBot(session,"username","password")
scraper.get_data()

python inheritence call new url after overriding

In the following code even after overiding the init function it still calls the old url,i want the new url contents instead how to do this
import urllib
import json
class process():
def processdata(self):
for di in results:
super(processcontent,self).__init__(new_url)
new_obj = getcontent.getdata()
print new_obj
break
if __name__ == '__main__':
url = "someurl"
p = processcontent(url)
#print p.getdata()
p.processdata()
First, you should not call __init__ after initialization, second, you don't use the attribute self.url but the global variable url.
Since url isn't a real attribute, you should call getdata with a parameter url:
import urllib
import json
class processcontent(Basecontent,getcontent):
def processdata(self):
obj = self.getdata(self.url)
results = obj["response"]
for di in results:
new_url = di["clusterUrl"]
new_obj = self.getdata(new_url)
print new_obj
break
def main():
url = "someurl"
p = processcontent(url)
#print p.getdata()
p.processdata()
if __name__ == '__main__':
main()
class getcontent(object):
#classmethod
def getdata(self):
response = urllib.urlopen(url) # <--- module-level variable
content = json.loads(response.read())
return content
You are referencing a module-level variable url in your getdata() method.

Categories

Resources