decoding_fn never gets called
Describe the bug decoding_fns never trigger
To Reproduce
#!/usr/bin/env python3
from functools import wraps
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives.serialization import load_pem_private_key
import simple_parsing
from dataclasses import dataclass
from collections.abc import Callable
from simple_parsing.helpers.serialization import register_decoding_fn
class Secret[T]:
def __init__(self, value: T) -> None:
self.value = value
def __repr__(self) -> str:
return rf"Secret(value={self.value!r})"
def parse_secret[T](parse: Callable[[str], T]) -> Callable[[str], Secret[T]]:
@wraps(parse)
def wrapper(s: str):
print("Parsing secret")
return Secret(parse(s))
return wrapper
def parse_ed25519(s: str) -> Ed25519PrivateKey:
print("Parsing ed25519")
key = load_pem_private_key(s.encode(), None)
match key:
case Ed25519PrivateKey():
return key
case _:
raise ValueError("Provided PEM key is not ed25519")
register_decoding_fn(Secret, parse_secret(parse_ed25519))
@dataclass(slots=True)
class Config:
# ed25519 private key used to sign api keys
key: Secret[Ed25519PrivateKey] | None = simple_parsing.field(
default=None,
decoding_fn=parse_secret(parse_ed25519),
)
print(simple_parsing.parse(Config, args=["--key", "test"]))
Expected behavior The arg should be passed to parse_secret and raise an exception.
Actual behavior The key is parsing without any issues.
» ./test.py
Config(key=Secret(value='test'))
Desktop (please complete the following information):
- Version 0.1.5
- Python version: 3.12.2
Fully acknowledge that I may have a total misunderstanding of the intent, but I did find a workaround. Given that this is totally undocumented and relies on some particular implementation details, I'm guessing this isn't the intended solution.
#!/usr/bin/env python3
from functools import wraps
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives.serialization import load_pem_private_key
import simple_parsing
from dataclasses import dataclass
from collections.abc import Callable
from simple_parsing.helpers.serialization import register_decoding_fn
class Secret[T]:
def __init__(self, value: T) -> None:
self.value = value
def __repr__(self) -> str:
return rf"Secret(value={self.value!r})"
def parse_secret[T](parse: Callable[[str], T]) -> Callable[[str], Secret[T]]:
@wraps(parse)
def wrapper(s: str):
print("Parsing secret")
return Secret(parse(s))
return wrapper
def parse_ed25519(s: str) -> Ed25519PrivateKey:
print("Parsing ed25519")
key = load_pem_private_key(s.encode(), None)
match key:
case Ed25519PrivateKey():
return key
case _:
raise ValueError("Provided PEM key is not ed25519")
register_decoding_fn(Secret, parse_secret(parse_ed25519))
@dataclass(slots=True)
class Config:
# ed25519 private key used to sign api keys
key: Secret[Ed25519PrivateKey] | None = simple_parsing.field(
default=None,
metadata={"custom_args": {"type": parse_secret(parse_ed25519)}},
)
print(simple_parsing.parse(Config, args=["--key", "test"]))
Hey there, the decoding_fn is called when serializing / deserializing a dataclass, whereas the type arg of argparse (which you're using here) is used when parsing the field / dataclass from the command-line.
Sorry if this isn't clear from the code / very limited docstrings.