|
1 | 1 | """ Functions for decoding dataclass fields from "raw" values (e.g. from json).
|
2 | 2 | """
|
| 3 | +import inspect |
3 | 4 | import warnings
|
4 | 5 | from collections import OrderedDict
|
5 | 6 | from dataclasses import Field, fields
|
6 | 7 | from functools import lru_cache, partial
|
7 | 8 | from logging import getLogger
|
8 | 9 | from typing import TypeVar, Any, Dict, Type, Callable, Optional, Union, List, Tuple, Set
|
9 | 10 |
|
| 11 | + |
10 | 12 | from simple_parsing.utils import (
|
11 | 13 | get_type_arguments,
|
12 | 14 | is_dataclass_type,
|
@@ -94,6 +96,35 @@ def get_decoding_fn(t: Type[T]) -> Callable[[Any], T]:
|
94 | 96 | # cache_info = get_decoding_fn.cache_info()
|
95 | 97 | # logger.debug(f"called for type {t}! Cache info: {cache_info}")
|
96 | 98 |
|
| 99 | + if isinstance(t, str): |
| 100 | + # Type annotation is a string. |
| 101 | + # This can happen when the `from __future__ import annotations` feature is used. |
| 102 | + potential_keys: List[Type] = [] |
| 103 | + for key in _decoding_fns: |
| 104 | + if inspect.isclass(key): |
| 105 | + if key.__qualname__ == t: |
| 106 | + # Qualname is more specific, there can't possibly be another match, so break. |
| 107 | + potential_keys.append(key) |
| 108 | + break |
| 109 | + if key.__qualname__ == t: |
| 110 | + # For just __name__, there could be more than one match. |
| 111 | + potential_keys.append(key) |
| 112 | + |
| 113 | + if not potential_keys: |
| 114 | + raise ValueError( |
| 115 | + f"Couldn't find a decoding function for the string annotation '{t}'.\n" |
| 116 | + f"This is probably a bug. If it is, please make an issue on GitHub so we can get " |
| 117 | + f"to work on fixing it." |
| 118 | + ) |
| 119 | + if len(potential_keys) == 1: |
| 120 | + t = potential_keys[0] |
| 121 | + else: |
| 122 | + raise ValueError( |
| 123 | + f"Multiple decoding functions registered for a type {t}: {potential_keys} \n" |
| 124 | + f"This could be a bug, but try to use different names for each type, or add the " |
| 125 | + f"modules they come from as a prefix, perhaps?" |
| 126 | + ) |
| 127 | + |
97 | 128 | if t in _decoding_fns:
|
98 | 129 | # The type has a dedicated decoding function.
|
99 | 130 | return _decoding_fns[t]
|
@@ -175,8 +206,9 @@ def get_decoding_fn(t: Type[T]) -> Callable[[Any], T]:
|
175 | 206 | # Unknown type.
|
176 | 207 | warnings.warn(
|
177 | 208 | UserWarning(
|
178 |
| - f"Unable to find a decoding function for type {t}. " |
179 |
| - f"Will try to use the type as a constructor." |
| 209 | + f"Unable to find a decoding function for the annotation {t} (of type {type(t)}). " |
| 210 | + f"Will try to use the type as a constructor. Consider registering a decoding function " |
| 211 | + f"using `register_decoding_fn`, or posting an issue on GitHub. " |
180 | 212 | )
|
181 | 213 | )
|
182 | 214 | return try_constructor(t)
|
|
0 commit comments