Access self attributes without declaring self in class function - python

im working on a python package and I wonder how can I declare a class, which receive some attributes in the init function and then be able to use that 'self' attributes in the rest of the functions without declaring self as it's parameters.
Here is an example code to make it easier:
class API():
def __init__(self, token):
self.token = token
def info():
headers = {'Token': f'{self.token}'}
response = requests.post(some_url, headers=headers)
return response
I didn't put self in info() function because that function is going to be called from the outside, but it will be great be able to reuse the token attribute received in the class initialization. i don't know if I'm missing something so any suggestion will be much appreciated.
Edit
If I use my current code, I get an error because using self keyword without declaring it on the function class, but if I put it, then when I make the function call I can pass self argument.

self is not a keyword; it's just a conventional name for the instance of API that is passed to info when it is called as an instance method.
You can't call info without such an instance.
class API():
def __init__(self, token):
self.token = token
def info(self):
headers = {'Token': f'{self.token}'}
response = requests.post(some_url, headers=headers)
return response
a = API("some token")
a.info()
a.info() is roughly equivalent to API.info(a).

Related

Python decorator class with members

I want to write a class that will have member variables and member functions that will be used as decorators.
class decorator_class:
def __init__(self, baseurl, username,password):
self._baseurl = baseurl
self._username = _username
self._password = _password
def decorator_get(self,arguments):
def inner_function(function):
#wraps(function)
def wrapper(*args, **kwargs):
url = self._url + argument
if len(kwargs) > 0:
url+="?"
argseperator=""
for k,v in kwargs.items():
url+="{}{}={}".format(argseperator,k,v)
argseperator="&"
r = requests.get(url, auth=(self._username, self._password))
if r.status_code != 200:
raise Exception('Failed to GET URL: {}'.format(url))
return function(args[0],json = r.json())
return wrapper
return inner_function
class usedecorator:
def __init__(baseurl, self,user,password):
self.dec = decorator_class(baseurl, self,user,password)
#dec.decorator_get('/path/to/resource1')
def process_output_resource1(self, json):
do_something_with_json
The problem is that __init__ is being called after the class is loaded and at that time dec is undefined.
if I define the decorator_class globally it works, but then there is no way to pass the url, user and password to it at runtime.
Any suggestions?
Your decorator_get > innder_function > wrapper has the userdecorator's self.
Weird sentence but eh.
You have some weird namings, IDK why did you use self as a second argument for instance but, I tried to follow your naming.
def decorator_get(arguments):
def inner_function(function):
#wraps(function)
def wrapper(self, *args, **kwargs):
url = self._base_url + arguments
if len(kwargs) > 0:
url+="?"
argseperator=""
for k,v in kwargs.items():
url+="{}{}={}".format(argseperator,k,v)
argseperator="&"
r = requests.get(url, auth=(self._username, self._password))
if r.status_code != 200:
raise Exception('Failed to GET URL: {}'.format(url))
return function(self, json = r.json())
return wrapper
return inner_function
class usedecorator:
def __init__(self, baseurl,user,password):
self._base_url = baseurl
self._username = user
self._password= password
#decorator_get('/path/to/resource1')
def process_output_resource1(self, json):
do_something_with_json
Indeed -to have a decorator for methods in a class, it must already be defined (i.e. ready to be used) when the method to be decorated is declared: which means it have to be declared either at top-level or inside the class body.
Code inside methods, including __init__, however will only run when an instance is created - and that is the point where the class will get your connection parameters.
If this decorator is being used always in this model, you can turn it into a descriptor: an object which is a class attribute, but which has code (in a method named __get__) that is executed after the instance is created.
This descriptor could then fetch the connection parameters in the instance itself, after it has been created, and prepare way for calling the underlying method.
That will require some reorganization on your code: the object returned by __get__ has to be a callable which will ultimately run your function, but it would not be nice if simply retrieving the method name would trigger the network request - one will expect it to be triggered when the process_output... method is actually called. The __get__ method then should return your inner "wrapper" function, which will have all the needed data for the request from the "instance" attribute Python passes automatically, but for the payload which it gets via kwargs.
class decorator_class:
def __init__(self, path=None):
self.path = None
self.func = None
def __get__(self, instance, owner):
if instance is None:
return self
def bound_to_request(**kwargs):
# retrieves the baseurl, user and password from the host instance:
# build query part of the target URL - instead of your convoluted
# code to build the query string (which will break ont he first special character,
# just pass kwargs as the "params" argument)
response = requests.get(instance._base_url, auth=(
instance.user, instance.password), params=kwargs)
# error treatment code
#...
return self.func(response.json())
return bound_to_request
def __call__(self, arg):
# create a new instance of this class on each stage:
# first anotate the API path, on the second call annotate the actual method
new_inst = type(self)()
if not self.path:
if not isinstance(arg, str):
raise TypeError("Expecting an API path for this method")
new_inst.path = arg
else:
if not callable(arg):
raise TypeError("Expecting a target method to be decorated")
new_inst.func = wraps(arg)
return new_inst
def __repr__(self):
return f"{self.func.__name__!r} method bound to retrieve data from {self.path!r}"
class use_decorator:
dec = decorator_class()
def __init__(self=, baseurl, user, password):
# the decorator assumes these to be set as instance attributes
self.baseurl = baseurl
self.user = user
self.password = password
# <- the call passing the path returns an instance of
# the decorator with the path set. it is use as an
# decorator is called again, and on this second call, the decorated method is set.
#dec.decorator_get('/path/to/resource1')
def process_output_resource1(self, json):
# do_something_with_json
...
In time, re-reading your opening paragraph, I see you intended to have more than one decorator inside your original class, probably others intended for "POST" and other HTTP requests: most important thing, the __get__ name here has nothing to do with HTTP: it is a fixed method name in the Python spec which is called automatically by the language when one will retrieve your method from an instance of use_decorator. That is, when there is code: my_instance.process_output_resource1(...), the __get__ method of the descriptor is called. Whatever it returned is then called.
For enabling the same decorator to use POST and other HTTP methods, I suggest you to have as a first parameter when annotating the path for each method, and then simply call the appropriate requests method by checking self.method inside the bound_to_request function.
I think you're going too far with the decorator approach. Let's break this down into a single question: What is the actual shared state here that you need a class for? To me, it looks like just the baseurl, user, and password. So let's just use those directly without a decorator:
from requests import Session
from requests.auth import HTTPBasicAuth
class UseDecorator: # this isn't a good name, but we will keep it temporarily
def __init__(self, baseurl, user, password):
self.baseurl = baseurl
self.session = Session()
# we've now bound the authentication to the session
self.session.auth = HTTPBasicAuth(user, password)
# now let's just bind a uri argument to a function to simply
# send a request
def send_request(self, uri, *args, **kwargs):
url = self.baseurl + uri
# you don't need to manually inject parameters, just use
# the params kwarg
r = self.session.get(url, params=kwargs)
# this will check the response code for you and even handle
# a redirect, which your 200 check will fail on
r.raise_for_status()
return r.json()
# then just handle each individual path
def path_1(self, *args, **kwargs):
data = self.send_request('/path/1', *args, **kwargs)
# process data
def path_2(self, *args, **kwargs):
data = self.send_request('/path/2', *args, **kwargs)
# process data
Because we're leveraging the machinery offered to us by requests, most of your decorator is simplified, and we can boil it down to a simple function call for each path

How to get `self` in http Google Cloud Function

I'm trying to use Firebase authentication in a Python HTTP Google Cloud function.
But the function verify_id_token() requires self as an argument. How do I get self in an HTTP Google Cloud Function? This is my current method:
def main(request):
print(self)
# Handle CORS
if request.method == 'OPTIONS':
# Allows GET requests from any origin with the Content-Type
# header and caches preflight response for an 3600s
headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Max-Age': '3600'
}
return '', 204, headers
headers = {
'Access-Control-Allow-Origin': '*'
}
# Validate Firebase session
if 'authorization' not in request.headers:
return f'Unauthorized', 401, headers
authorization = str(request.headers['authorization'])
if not authorization.startswith('Bearer '):
return f'Unauthorized', 401, headers
print(authorization)
id_token = authorization.split('Bearer ')[1]
print(id_token)
decoded_token = auths.verify_id_token(id_token)
uid = str(decoded_token['uid'])
if uid is None or len(uid) == 0:
return f'Unauthorized', 401, headers
I already tried adding self as a parameter to the main function, but that does not work since request has to be the first parameter and no second parameter is set, so neither def main(self, request) nor def main(request, self) work.
main is a method and not a class. Methods that are not members of a class do not have self.
self is a reference to object itself. Let's say you have a class with properties (methods, attributes). If you want to access to any one of properties inside the class itself, you'll need self (some languages call it this. Such as JavaScript).
If you create an object from that class and want to access to any one of properties you would use the object name.
Example:
class MyClass:
def __init__(self):
pass
def method1(self):
print("Method 1 is called")
def method2(self):
print("I'll call method 1")
self.method1()
See, if one wants to call method2 from method1 they will need self.
But if you create an object from MyClass you can access any property using the variable name:
mc = MyClass()
mc.method1()
TL;DR
You can't (and you do NOT need to) access self outside the scope of a class.

Python: run staticmethod of class in init?

The following is making me suspect whether what I want is a class or a module.
Basically I'm building a parser of sorts, an API library.
The external service which my code connects to needs a token for every time a request is made.
I'm successfully generating this token. However, I'm not sure how I can "give" this token to class instances if it's not in the __init__ of the class.
My code so far:
class MBParser(object):
pass
class SomeServiceParser(MBParser):
'''instantiate and use me'''
def __init__(self):
self.token = _get_token()
#staticmethod
def _get_token():
# code to get the token
You could wrap each function that requires a new token in a decorator like the following:
def new_token(func):
def wrapper(self, *args, **kwargs):
self.token = SomeParser._get_token()
r = func(self, *args, **kwargs)
return wrapper
I explicitly added a self argument in the wrapper method since the methods in the class will need this anyway. And this way, I can access the self.token attribute and set it to a new value
Could use the decorator as follows:
#new_token
def makeHTTPRequest(self, name):
# ... make request and use self.token here

Mocking attributes using #patch decorator

I am struggling with mocking attributes in my Python tests. The function I am trying to test keeps failing because the mock probably returns the right value but is the wrong type (Should be string and it is a MagicMock instead.
I have found this answer and I understand I need to use a PropertyMock. But I can't get it to work neither with the context manager or using the #patch decorator. Mock attributes in Python mock?
Here is my test:
#patch('keys.views.requests.post')
#patch('keys.views.requests.Response.text', new_callable=PropertyMock)
def test_shows_message_when_receives_error(self, mock_response_text ,mock_post):
expected_error = escape(MESSAGE)
data_to_be_received = json.dumps({
"message":"Bad Request",
"errors":[{
"resource":"Application",
"field":"client_id",
"code":"invalid"
}]
})
mock_response_text.return_value = data_to_be_received
response = self.client.get('/users/tokenexchange?state=&code=abc123')
self.assertContains(response, expected_error)
And the code I am testing:
def token_exchange(request):
parameters = {'client_id': '##', 'client_secret': '##', 'code': code}
response = requests.post('https://www.strava.com/oauth/token', parameters)
data_received = json.loads(response.text)
if 'errors' not in data_received:
return HttpResponse(response.text)
else:
return render(request, 'home.html', {'error': STRAVA_AUTH_ERROR})
The error I keep getting is:
File "##", line 66, in token_exchange
data_received = json.loads(response.text)
TypeError: the JSON object must be str, bytes or bytearray, not 'MagicMock'
Thanks for your answers!!!
keys.views.requests.Response.text is highly likely a instance variable which cannot and should not be mocked using PorpertyMock
Here is quote from documentation:
Generally speaking, instance variables are for data unique to each instance and class variables are for attributes and methods shared by all instances of the class:
https://docs.python.org/2/tutorial/classes.html
class Dog:
kind = 'canine' # class variable shared by all instances
def __init__(self, name):
self.name = name # instance variable unique to each instance
How to mock a python class instance_variable?
I had a solution which copied from somewhere, it worked by too tedious:
Python mock a base class's attribute
In your specific case, mock Response class instead of the text instance
#patch('keys.views.requests.post')
#patch('keys.views.requests.Response')
def test_shows_message_when_receives_error(self, mock_response ,mock_post):
mock_post.return_value = None # mock the func
mock_response.text = mock.Mock(text=data_to_be_received).text

Call external function without sending 'self' arg

I'm writing tests for a Django application and using a attribute on my test class to store which view it's supposed to be testing, like this:
# IN TESTS.PY
class OrderTests(TestCase, ShopTest):
_VIEW = views.order
def test_gateway_answer(self):
url = 'whatever url'
request = self.request_factory(url, 'GET')
self._VIEW(request, **{'sku': order.sku})
# IN VIEWS.PY
def order(request, sku)
...
My guess is that the problem I'm having is caused because since I'm calling an attribute of the OrderTests class, python assumes I wanna send self and then order get the wrong arguments. Easy to solve... just not use it as a class attribute, but I was wondering if there's a way to tell python to not send self in this case.
Thanks.
This happens because in Python functions are descriptors, so when they are accessed on class instances they bind their first (assumed self) parameter to the instance.
You could access _VIEW on the class, not on the instance:
class OrderTests(TestCase, ShopTest):
_VIEW = views.order
def test_gateway_answer(self):
url = 'whatever url'
request = self.request_factory(url, 'GET')
OrderTests._VIEW(request, **{'sku': order.sku})
Alternatively, you can wrap it in staticmethod to prevent it being bound to the instance:
class OrderTests(TestCase, ShopTest):
_VIEW = staticmethod(views.order)
def test_gateway_answer(self):
url = 'whatever url'
request = self.request_factory(url, 'GET')
self._VIEW(request, **{'sku': order.sku})

Categories

Resources