44import inspect
55import itertools
66import logging
7+ import operator
78import os
89import shutil
910import struct
11+ import sys
1012import tempfile
1113import typing as ty
1214from collections import Counter
1618from pathlib import Path
1719from warnings import warn
1820
21+ if sys .version_info >= (3 , 10 ):
22+ from typing import TypeAlias
23+ else :
24+ from typing_extensions import TypeAlias
25+
1926from fileformats .core .typing import Self
27+ from fileformats .core .utils import _excluded_subpackages
2028
2129from .classifier import Classifier
2230from .datatype import DataType
4351 from .converter_helpers import Converter
4452
4553
54+ class SupportsDunderLT (ty .Protocol ):
55+ def __lt__ (self , other : ty .Any ) -> bool :
56+ ...
57+
58+
59+ class SupportsDunderGT (ty .Protocol ):
60+ def __gt__ (self , other : ty .Any ) -> bool :
61+ ...
62+
63+
64+ FileSetPrimitive : TypeAlias = ty .Union [os .PathLike [str ], ty .Sequence [os .PathLike [str ]]]
65+
66+
4667FILE_CHUNK_LEN_DEFAULT = 8192
4768
4869
@@ -542,7 +563,7 @@ def convert(
542563 def get_converter (
543564 cls ,
544565 source_format : ty .Type [DataType ],
545- ) -> "Converter | None " :
566+ ) -> "ty.Optional[ Converter] " :
546567 """Get a converter that converts from the source format type
547568 into the format specified by the class
548569
@@ -572,12 +593,9 @@ def get_converter(
572593 return None
573594 # trigger loading of standard converters for target format
574595 converters = cls .get_converters_dict ()
575- try :
576- unclassified = source_format .unclassified # type: ignore
577- except AttributeError :
578- import_extras_module (source_format )
579- else :
580- import_extras_module (unclassified )
596+ # import extras modules
597+ source_format ._import_extras_module () # type: ignore[attr-defined]
598+ cls ._import_extras_module ()
581599 try :
582600 converter = converters [source_format ]
583601 except KeyError :
@@ -613,6 +631,15 @@ def get_converter(
613631 converters [source_format ] = converter
614632 return converter
615633
634+ @classmethod
635+ def _import_extras_module (cls ) -> None :
636+ try :
637+ unclassified = cls .unclassified # type: ignore
638+ except AttributeError :
639+ import_extras_module (cls )
640+ else :
641+ import_extras_module (unclassified )
642+
616643 @classmethod
617644 def get_converters_dict (
618645 cls , klass : ty .Optional [ty .Type [DataType ]] = None
@@ -633,15 +660,17 @@ def get_converters_dict(
633660 @classmethod
634661 def convertible_from (
635662 cls ,
636- only_namespace_parents : bool = True ,
637- union_sort_key : ty .Callable [[DataType ], ty .Any ] = attrgetter ("__name__" ),
663+ union_sort_key : ty .Callable [
664+ [ty .Type [DataType ]],
665+ ty .Union [SupportsDunderLT , SupportsDunderGT ],
666+ ] = attrgetter ("__name__" ),
638667 ) -> ty .Type ["DataType" ]:
639668 """Union of types that can be converted to this type, including the current type.
640669 If there are no other types that can be converted to this type, return the current type
641670
642671 Parameters
643672 ----------
644- only_namespace_parents : bool
673+ include_generic : bool
645674 If True, only consider parent classes in the same namespace for conversion.
646675 union_sort_key : callable[[DataType], Any], optional
647676 A function used to sort the union of types. Defaults to sorting by the type name.
@@ -652,13 +681,14 @@ def convertible_from(
652681 The type or union of types that can be converted to this type.
653682 """
654683
655- ns = cls .namespace
656684 datatypes : ty .List [ty .Type [DataType ]] = [cls ]
657- for fformat in FileSet .subclasses ():
658- if issubclass (cls , fformat ) and (
659- fformat .namespace == ns or not only_namespace_parents
660- ):
661- datatypes .extend (fformat .get_converters_dict ().keys ())
685+ cls ._import_extras_module ()
686+ exclude_subpackages = copy (_excluded_subpackages )
687+ exclude_subpackages .discard (cls .namespace )
688+ for subcls in FileSet .subclasses (exclude = exclude_subpackages ):
689+ if issubclass (subcls , cls ):
690+ subcls ._import_extras_module ()
691+ datatypes .extend (subcls .get_converters_dict ().keys ())
662692 if len (datatypes ) == 1 :
663693 return cls
664694 concrete_datatypes = set ()
@@ -675,8 +705,8 @@ def add_concrete(datatype: ty.Type[DataType]) -> None:
675705 for datatype in datatypes :
676706 add_concrete (datatype )
677707 # TODO: Might want to sort datatypes in a more specific order
678- return ty . Union . __getitem__ ( # pyright: ignore[reportAttributeAccessIssue]
679- tuple ( sorted (concrete_datatypes , key = union_sort_key ) )
708+ return functools . reduce (
709+ operator . or_ , sorted (concrete_datatypes , key = union_sort_key )
680710 ) # type: ignore[return-value]
681711
682712 @classmethod
0 commit comments