I am writing a class that stores credentials. The credential can have a password_getter attribute which stores a lambda that will get the password on-demand. This getter needs access to self.username in order to function. How can this be accomplished? Consider the following setup:
class Credential:
"""Defines one username and password combination, and how to get them."""
def __init__(self, username: str, password_getter: callable):
self.username = username
self._password_getter = password_getter
#property
def password(self) -> str:
self.password = self._password_getter()
return self.password
Then we define a new credential:
cred = Credential(
username="test",
password_getter=lambda self: self.username+"_pass",
)
assert cred.password == "test_pass"
The above is non-functional due to self not existing during definition, but it suggests what I'm trying to do: Access the current value of cred.username from inside the lambda.
For some context as to why this is happening; the function in production actually goes out to a password vault api and requests the password for the given username.
The Callable you pass in has no intrinsic connection to the instance, so your two options are to give it implicit or explicit access. Implicit would rely on simple closures:
cred = Credential(
username="test",
password_getter=lambda: cred.username + "_pass",
)
The problem here is if you reassign cred later and it refers to the wrong instance when trying to get the password.
The explicit alternative is:
cred = Credential(
username="test",
password_getter=lambda instance: instance.username + "_pass",
)
...
#property
def password(self) -> str:
self.password = self._password_getter(self)
The class explicitly passes itself to the password getter. So it should be typed as Callable[[Credentials], str], i.e. something that receives an instance of the class as argument and is expected to return a string.
Related
This question already has answers here:
What is the purpose of the `self` parameter? Why is it needed?
(26 answers)
Closed 4 months ago.
I am trying to write a class that allows users to register on a platform with a username, but checks that this username does not exist in the database.
I have this code:
class Freelancer:
"""Leaving this blank for now while I explore the functionality """
number_of_sales = 0
available_niches = ["Graphic design", "Art", "Data Analysis", "Music", "Business", "Writing and Translations"]
usernames = []
def __init__(self, username):
self.username = _check_username(username)
def _check_username(self, username):
if self.username in Freelancer.usernames:
print("This username already exist on our database, consider choosing another one")
else:
self.username = username
Freelancer.usernames.append(self.username)
print("You have successfully setup a username on our platform")
which I tested like so:
David = Freelancer("dave23")
But I got an exception: NameError: name '_check_username' is not defined. What is wrong with the code? How can I call a private method from __init__?
You missed self before calling private method.
def __init__(self, username):
self.username = self._check_username(username)
If it is still gives error like: 'Freelancer' object has no attribute 'username'
define username variable
First of all, __init__ is not the constructor.
Note that __init__(self) already has the object (self), so it was already constructed before calling __init__. (The real constructor in Python is __new__).
Python does not have private methods, no matter how many underscores you add.
This should work:
def __init__(self, username):
self.username = self.check_username(username)
def check_username(self, username):
...
You could add the leading underscore to indicate that the method is for internal use only.
There is however another mistake in the code. It should be:
def _check_username(self, username):
if self.username in self.usernames: # <-----
print("This username already exist on our database, consider choosing another one")
else:
self.usernames.append(self.username) # <-----
return username
You are manipulating the class. That means that if the class is instantiated again usernames is not empty.
I am working with locust and I am working in mimicking the behavior of a user. However I am getting trouble accessing the parent class variable. Any idea how I can pass it?
class User(TaskSet):
some_user = ''
def on_start(self):
self.get_user()
def get_user(self):
some_user = self.client.get...#gets user
#task
class UpdatingUser(TaskSet):
def updating(self):
path = "/posts/" + User.some_user
By the time I get to User.some_user I never have the user.
You've not provided all of the code, but the problem may be that get_user() is setting some_user as an instance attribute somewhere, as in self.some_user = foo.
This will only set some_user for that specific instance of User however (so for Bob, Lisa, Beto, User53, etc.), but not for the User class itself. When accessing some_user with self, as in self.some_user, you set it for the specific instance that's executing those statements, not the class. In updating() you're accessing the class attribute User.some_user, not a specific instance attribute like usr53.some_user. In order to update the class attribute, invariant by default for all instances of User, you ought to be setting it with User.some_user = foo in get_user().
Right now in path = "/posts/" + User.some_user, it's trying to access the class attribute which may never have been set. Because nested classes like UpdatingUser can't access the instances of the nesting class (User) that they're called from, UpdatingUser won't be able to access any some_user set with self or any other instance attributes of User. So the solution would be to have get_user() set the class attribute instead of the instance attribute as described in the previous paragraph.
This answer is a bit late but, if anyone has this issue, the TaskSet has a parent property, which can be used to access the parent's instance variables. The following is what I used for a basic one-time login:
class UserBehaviour(TaskSet):
def on_start(self):
self.token = self.login()
self.headers = {'Authorization': 'Bearer ' + self.token}
def login(self):
with self.client.post("/login", catch_response = True) as response:
return response.json()['token']
#task
class UserTask1(TaskSet):
#task
def get_data(self):
self.client.get("/data", headers = self.parent.headers)
class WebsiteUser(HttpLocust):
task_set = UserBehaviour
Is there any way to attach a decorator to the following function in Python 3 in order to save having to call _is_valid_token()? I will be passing this static value into every function as a key to call the API via SSL and wish to simplify my code.
# Constants
TOKEN = '7632ba6a-0609-4b0c-a92e-9107bec88941'
#app.route('/my-game-server/api/v1.0/create_new_game/<token>/<player1>/<player2>/<rules>',
methods=['GET'])
def create_new_game(token, player1, player2, rules):
# Create
"""Creates a new game from scratch
:param token: Unique value that verifies the caller is valid
:param player1: Name of the first player
:param player2: Name of the second player
:param rules: String representing the rules to be used in the game
:return: UUID representing the game id that has been created
"""
# Validate token
_is_valid_token(token)
# Create game
game_id = str(uuid.uuid4())
return game_id
def _is_valid_token(token):
"""Validate that the caller has a legitimate call into the service
:param token: Unique value that verifies the caller is valid
"""
if token != TOKEN:
abort(400)
You can use a url processor.
#app.url_value_preprocessor
def _is_valid_token(endpoint, values):
if 'token' not in values:
return
if values['token'] != TOKEN:
abort(400)
This runs for all routes, but only does the validation if the route actually has a 'token' value. There are of course many other checks you could do beforehand to limit validation, such as basing it on specific endpoint names, but this is the most general function.
You can also just decorate the specific functions you want to validate. This would be more general than the Flask solution.
def _is_valid_token(f):
#wraps(f)
def decorated(token, *args, **kwargs):
if token != TOKEN:
abort(400)
return f(token, *args, **kwargs):
return decorated
#app.route(...)
#_is_valid_token
def create_new_game(token, ...):
...
You can always define your own function decorator! This article talks more about decorators in Python:
http://python-3-patterns-idioms-test.readthedocs.org/en/latest/PythonDecorators.html
You could define your decorator as a function like so:
def validate_token(func):
def newfunc(token, *args):
_is_valid_token(token)
return func(token, *args)
return newfunc
...
#validate_token
def create_new_game(token, player1, player2, rules):
...
Now whenever create_new_game is called, it'll call _is_valid_token first, as long as it has the decorator.
I am trying to achieve the following:
class A:
username = None
username = get_username()
def get_username(self):
if username is None:
try:
uname = os.environ["USER"]
except:
printf("Couldn't find a user name")
return uname
return username
Not sure how to achieve this. I'm sure I'm missing some "self." prefixes but this is the first time I'm working with python and static members.
In a sense I want a class with some members and functions to calculate values for these members but I don't want recalculations. I would also like these to be static functions and data members.
The problem is that the line "username = get_username()" the function hasn't already been defined. If I put username after the function then it's not
First, there's no reason to assign None to username if you're just going to reassign it immediately after.
Second, if you want the method to be a static method, you can't give it a self argument. And if you want a real static method, you have to declare it explicitly.
#staticmethod
def get_username():
if username is None:
...
Otherwise, you need an instance of the class (that self) to call it on, and you don't have one yet.
In Python 3, any regular method acts like a static method when called on the class, like an instance method when called on an instance. So, if you're sure you're never going to want to call a.get_username() on an instance a, you can skip the decorator. But you still need to get rid of the self parameter.
I think what you're actually trying to do is use a class variable to memoize the result of a static method. You can't do that, but you can use a class variable to memoize the result of a class method, which may be close enough. That would look like this:
class A:
username = None
#classmethod
def get_username(cls):
if cls.username is None:
try:
uname = os.environ["USER"]
except:
print("Couldn't find a user name")
else:
cls.username = uname
return cls.username
On the other hand, there's no good reason username has to be a class member. You can memoize by adding a member to the function, by passing a mutable default variable, or in various other ways which don't require infecting the class, and which allow you to leave get_username as a static method instead of a class method.
But really, the best solution is to find a memoization library on PyPI, in ActiveState's recipe list, etc., so you can just write this:
class A:
#memoize
#staticmethod
def get_username():
try:
return os.environ["USER"]
except:
print("Couldn't find a user name")
return None
Again, you can drop the #staticmethod if you're sure nobody's ever going to try to create an instance of A and call get_username on it.
if you don't want to lose the ability to refer to A.username, you can use class property with a little bit of metaclass:
class A(type):
def __new__(cls, name, bases, attrs):
# this allows B().username to also work
attrs['username'] = property(lambda s: s.__class__.username)
return type.__new__(cls, name, bases, attrs)
#property
def username(self):
if not hasattr(self, '_username'):
self._username = 'bar'
return self._username
class B(object):
__metaclass__ = A
print B.username
print B().username
In python code I often see the use of #property.
If I understand correctly, with the property function a getter setter and deleter can be defined.
Why would one use #property if the setter and deleter are not defined (#x.setter, #x.deleter)? Isn't this the same as not using #property at all?
It creates an API that does not allow a value to be set. This is similar in other languages to a constant.
Defining a property with a getter function but without a setter can be very useful in certain scenarios. Lets say you have a model as below in django; a model is essentially a database table with entries called fields. The property hostname is computed from one or more fields in the model from the database. This circumvents needing another entry in the database table that has to be changed everytime the relevant fields are changed.
The true benefit of using a property is calling object.hostname() vs. object.hostname. The latter is passed along with the object automatically so when we go to a place like a jinja template we can call object.hostname but calling object.hostname() will raise an error.
The example below is a virtualmachine model with a name field and an example of the jinja code where we passed a virtualmachine object.
# PYTHON CODE
class VirtualMachine(models.Model):
name = models.CharField(max_length=128, unique=True)
#property
def hostname(self):
return "{}-{}.{}".format(
gethostname().split('.')[0],
self.name,
settings.EFFICIENT_DOMAIN
)
# JINJA CODE
...start HTML...
Name: {{ object.name }}
# fails
Hostname: {{ object.hostname() }}
# passes
Hostname: {{ object.hostname }}
...end HTML...
This is a good answer. Additionally, you can also modify the value of your property based on other kwargs and do this within the same method declaration. If you create a self._hostname instance variable, you can also modify the value based on other kwargs or variables. You can also obtain the value from your property and use it within other methods as self.scheme (see below) is syntactically pleasing and simple :).
class Neo4j(Database):
def __init__(self, label, env, username, password, hostname, port=None, routing_context=False, policy=None, scheme=None, certificate=None):
super().__init__(label, env)
self.username = username
self._password = password
self.hostname = hostname
self.port = port # defaults, 7687
self._scheme = scheme # example - neo4j, bolt
self.routing_context = routing_context # self.policy = policy policy=None,
self.policy = policy # Examples, europe, america
self.certificate = certificate # examples, None, +s, +ssc
#property
def scheme(self):
if not self.certificate:
return f'{self._scheme}'
return f'{self._scheme}+{self.certificate}'
def __repr__(self) -> str:
return f'<{self.scheme}://{self.hostname}:{self.port}>' #if self.ro
db = Neo4j(label='test', env='dec', username='jordan', password='pass', hostname='localhost', port=7698, scheme='neo4j', certificate='ssc')
print(db.scheme) >>> neo4j+ssc
TL;DR
So if you have heavy logic in the #property function, be aware that it will be running the entire logic each time you access the property. In this case I would suggest using a getter with a setter
Verbose
Another aspect which I don't feel has been explored is that the #property which is a getter, could be and most likely will be called multiple times where as the setter will most likely be called once when you instantiate the object.
IMO, this model should be used if the #property function is not doing too much heavy lifting. In the example below, we are just concatenating some strings to generate an email address.
class User:
DOMAIN = "boulder.com"
def __init__(self, first_name: str, last_name: str) -> None:
self.first_name = first_name
self.last_name = last_name
#property
def email(self) -> str:
return "{}_{}#{}".format(self.first_name, self.last_name, self.DOMAIN)
But if you are going to add some extended or heavy logic to the function, then I would recommend creating a getter for it so that it is only run once. For example, lets say we need to check whether the email is unique, this logic would be better served in a getter other wise you will run the logic to check for uniqueness of the email each time you want to access it.
class User:
DOMAIN = "boulder.com"
def __init__(self, first_name: str, last_name: str) -> None:
self.first_name = first_name
self.last_name = last_name
#property
def email(self) -> str:
return self._email
#email.setter
def email(self) -> None:
proposed_email = "{}_{}#{}".format(self.first_name, self.last_name, self.DOMAIN)
if is_unique_email(proposed_email):
self._email = proposed_email
else:
random_suffix = get_random_suffix()
self._email = "{}_{}_{}#{}".format(
self.first_name, self.last_name, random_suffix, self.DOMAIN
)