1

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.

6
  • 2
    You need to pass self when calling the getter, or you use lambda: cred.username + '_pass'. Commented Mar 25, 2022 at 9:11
  • The easiest way is to do : python username="test" cred = Credential( username="username", password_getter=lambda : username+"_pass", ) Commented Mar 25, 2022 at 9:15
  • The problem is that another part of the code could theoretically change the username later. The getter needs to be able to use the current username in its response. Commented Mar 25, 2022 at 9:32
  • so why isn't this fn just a method on Credential if it needs access to state stored on the instance? You can still call out to any 3rd party fn you need. Commented Mar 25, 2022 at 9:40
  • 1
    Because Credential just defines a framework for storing and retrieving usernames and passwords. The actual getter code varies significantly between credentials, ranging from checking environment variables to querying remote APIs. I could subclass Credential for each cred, but that just seemed bulky if I could use lambdas. Commented Mar 25, 2022 at 10:10

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Nice! I totally missed the obvious choice to pass self in self._password_getter(self). Thanks for that!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.