Repository: seandstewart/typical (language: python)

Issue:
issubclass error after version 2.6.4
* typical version: All versions after 2.6.4
* Python version: Tested with 3.7, 3.8, and 3.9
* Operating System: Linux

### Description

In all versions after 2.6.4 many of my tests that used to pass are now failing with the error:
```
___________________________________________________________________ TestRead.test_creation ___________________________________________________________________

self = <tests.test_calculate_fdc.TestRead testMethod=test_creation>

    def test_creation(self):
>       _ = tstoolbox.calculate_fdc(input_ts=self.fdata)

tests/test_calculate_fdc.py:17: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../pythons/working_3.8.0/lib/python3.8/site-packages/typic/api.py:189: in func_wrapper
    return func(*args, **kwargs)
src/tstoolbox/functions/calculate_fdc.py:207: in calculate_fdc
    xdat = ppf(tsutils.set_plotting_position(tmptsd.count(), plotting_position))
../../pythons/working_3.8.0/lib/python3.8/site-packages/typic/api.py:188: in func_wrapper
    args, kwargs = enforcer(*args, **kwargs)
../../pythons/working_3.8.0/lib/python3.8/site-packages/typic/serde/binder.py:168: in enforce_binding
    vargs[i] = __binding[i](v)
<typical generated deserializer_2446244414474070853>:11: in deserializer_2446244414474070853
    if issubclass(Literal, vtype):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

st = typing.Literal, t = <class 'str'>

    @lru_cache(maxsize=None)
    def cached_issubclass(st: Type, t: Union[Type, Tuple[Type, ...]]) -> bool:
        """A cached result of :py:func:`issubclass`."""
>       return issubclass(st, t)
E       TypeError: issubclass() arg 1 must be a class

../../pythons/working_3.8.0/lib/python3.8/site-packages/typic/util.py:504: TypeError

```


### What I Did

I actually don't know how to fix.

I looked through existing typical issues and looking at the commit descriptions to try an get a clue as to what I am doing wrong.




Discussion / hints:

Fix patch:
diff --git a/pyproject.toml b/pyproject.toml
index c55b357a..30d6f7e6 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
 [tool.poetry]
 name = "typical"
 packages = [{include = "typic"}]
-version = "2.7.3"
+version = "2.7.4"
 description = "Typical: Python's Typing Toolkit."
 authors = ["Sean Stewart <sean_stewart@me.com>"]
 license = "MIT"
@@ -157,7 +157,7 @@ requires = ["poetry>=0.12"]
 build-backend = "poetry.masonry.api"
 
 [bumpver]
-current_version = "v2.7.3"
+current_version = "v2.7.4"
 version_pattern = "vMAJOR.MINOR.PATCH[-TAGNUM]"
 commit_message = "bump version to {new_version} [ci skip]"
 tag = false
diff --git a/typic/__init__.py b/typic/__init__.py
index 4bcdfb0d..b4e0de44 100644
--- a/typic/__init__.py
+++ b/typic/__init__.py
@@ -13,4 +13,4 @@
 al = typed
 
 
-__version__ = "2.7.3"
+__version__ = "2.7.4"
diff --git a/typic/api.py b/typic/api.py
index a3cf8405..718907fc 100644
--- a/typic/api.py
+++ b/typic/api.py
@@ -364,7 +364,13 @@ def typed(
     ...
 
 
-def typed(_cls_or_callable=None, *, delay: bool = False, strict: bool = None):
+def typed(
+    _cls_or_callable=None,
+    *,
+    delay: bool = False,
+    strict: bool = None,
+    always: bool = None,
+):
     """A convenience function which automatically selects the correct wrapper.
 
     Parameters
@@ -373,6 +379,8 @@ def typed(_cls_or_callable=None, *, delay: bool = False, strict: bool = None):
         Optionally delay annotation resolution until first call.
     strict
         Turn on "validator mode": e.g. validate incoming data rather than coerce.
+    always
+        Whether classes should always coerce values on their attributes.
 
     Returns
     -------
@@ -382,8 +390,8 @@ def typed(_cls_or_callable=None, *, delay: bool = False, strict: bool = None):
 
     def _typed(obj: Union[Callable, Type[ObjectT]]):
         if inspect.isclass(obj):
-            return wrap_cls(obj, delay=delay, strict=strict)  # type: ignore
-        elif isinstance(obj, Callable):  # type: ignore
+            return wrap_cls(obj, delay=delay, strict=strict, always=always)  # type: ignore
+        elif callable(obj):  # type: ignore
             return wrap(obj, delay=delay, strict=strict)  # type: ignore
         else:
             raise TypeError(
@@ -657,7 +665,9 @@ def settings(
 
     def settings_wrapper(_cls):
         _resolve_from_env(_cls, prefix, case_sensitive, aliases)
-        cls = wrap_cls(dataclasses.dataclass(_cls, frozen=frozen), jsonschema=False)
+        cls = wrap_cls(
+            dataclasses.dataclass(_cls, frozen=frozen), jsonschema=False, always=False
+        )
         return cls
 
     return settings_wrapper(_klass) if _klass is not None else settings_wrapper
diff --git a/typic/serde/des.py b/typic/serde/des.py
index 33d7e456..ab419fad 100644
--- a/typic/serde/des.py
+++ b/typic/serde/des.py
@@ -736,8 +736,10 @@ def _build_generic_union_des(self, context: BuildContext):
             desers = {f"{n}_des": p.transmute for n, p in annos.items()}
             types = {n: p.annotation.resolved_origin for n, p in annos.items()}
             ctx: Mapping[str, Union[Type, DeserializerT]] = {**types, **desers}
-            names = (*annos,)
             for name in annos:
+                # Can't do subclass checks with these...
+                if name in {"Literal", "Final"}:
+                    continue
                 with func.b(f"if issubclass({name}, {self.VTYPE}):") as b:
                     b.l(f"return {name}_des({self.VNAME})")
             for name in desers:
@@ -748,9 +750,8 @@ def _build_generic_union_des(self, context: BuildContext):
             func.namespace.update(ctx)
             func.l(
                 "raise ValueError("
-                f'f"Value could not be deserialized into one of {names}: {{val!r}}"'
+                f'f"Value could not be deserialized into one of {(*annos,)}: {{val!r}}"'
                 ")",
-                names=names,
             )
             return False
 


Test patch:
diff --git a/tests/objects.py b/tests/objects.py
index 9c8a2725..4491aad0 100644
--- a/tests/objects.py
+++ b/tests/objects.py
@@ -232,12 +232,12 @@ def math(self, a: int) -> int:
         return a * a
 
 
-@typic.klass(delay=True)
+@typic.klass(delay=True, always=False)
 class KlassDelayed:
     foo: str
 
 
-@typic.al(delay=True)
+@typic.al(delay=True, always=False)
 @dataclasses.dataclass
 class Delayed:
     foo: str