Last active
March 12, 2024 14:07
-
-
Save qexat/cfa25c740d959aa6a68bc02bf019512f to your computer and use it in GitHub Desktop.
Python implementation of Solution 3 from Let's Get Rusty's "Improve your Rust APIs with the type state pattern" video
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
PoC of an equivalent Python implementation of the third solution borrowed from the following video: | |
<https://www.youtube.com/watch?v=_ccDqRTx-JU> | |
Important note: Python static type checkers are independent from the interpreter, and type checking failing | |
does not prevent from running code. Considering that, the following code does NOT perform runtime checks for | |
safety. `add_password()` is still technically accessible, even through a `PasswordManager` object in a locked | |
state (i.e. `PasswordManager[Literal[True]]`). | |
The program also requires the following dependencies: | |
- `result` (installable from the PyPI) | |
""" | |
from __future__ import annotations | |
from typing import Generic | |
from typing import Literal | |
from typing import TYPE_CHECKING | |
from typing import TypeVar | |
# pip install result | |
from result import Err | |
from result import Ok | |
from result import Result | |
LockState = TypeVar("LockState", Literal[True], Literal[False]) | |
class PasswordError(ValueError): | |
pass | |
class PasswordManager(Generic[LockState]): | |
@classmethod | |
def new( | |
cls: type[PasswordManager[Literal[True]]], | |
master_pass: str, | |
) -> PasswordManager[Literal[True]]: | |
return cls(master_pass) | |
def __init__( | |
self, | |
master_pass: str, | |
passwords: dict[str, str] = {}, | |
is_locked: LockState = True, | |
) -> None: | |
self.__master_pass = master_pass | |
self.__passwords: dict[str, str] = passwords | |
if TYPE_CHECKING: # <=> PhantomData, sort of | |
self.__is_locked = is_locked | |
def unlock( | |
self: PasswordManager[Literal[True]], | |
master_pass: str, | |
) -> Result[PasswordManager[Literal[False]], PasswordError]: | |
if self.__master_pass != master_pass: | |
return Err(PasswordError("invalid master password")) | |
return Ok(PasswordManager(self.__master_pass, self.__passwords, False)) | |
def lock(self: PasswordManager[Literal[False]]) -> PasswordManager[Literal[True]]: | |
return PasswordManager(self.__master_pass, self.__passwords, True) | |
def list_passwords(self: PasswordManager[Literal[False]]) -> dict[str, str]: | |
return self.__passwords | |
def add_password( | |
self: PasswordManager[Literal[False]], | |
username: str, | |
password: str, | |
) -> None: | |
self.__passwords[username] = password | |
def encryption(self) -> str: | |
todoǃ() | |
@property | |
def version(self) -> str: | |
return "1.0.0" | |
# Don't get fooled, it's not an exclamation mark, it's a retroflex click symbol x) | |
def todoǃ(): | |
raise SystemExit(1) | |
def main() -> None: | |
manager = PasswordManager.new("password123") | |
# manager.add_password("qexat", "abcdefg") # Pyright & MyPy report an error here, as expected | |
match manager.unlock("password123"): | |
case Ok(manager): | |
pass | |
case Err(e): | |
raise e | |
print(manager.list_passwords()) | |
manager.add_password("username", "gEnErIcS_<3") # No error reported here! | |
manager = manager.lock() | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The video: https://www.youtube.com/watch?v=_ccDqRTx-JU