Skip to main content

tools to make typing more consistent in older versions of python3, and access generic variables at runtime

Project description

RT-Generic

RT-Generic is a typed python module to make accessing the actual runtime types from a generic module more straight forward. It defines a base class RTGeneric which initializes each subclass (at subclass definition) with a mapping from (Class, TypeVar) to the actual type eventually assigned to that TypeVar. This permits me, for example, when creating class GenClass[T,U,V](RTGeneric): to write code which will behave appropriately for when I eventually create class SubClass1(GenClass[int, float, dict[int, float]]): and class SubClass2(GenClass[tuple[str], list[float], dict[tuple[str], list[float]]]):. Repository, PyPI.

The process has 4 steps minimum (with a few optional steps along the way):

  1. Import RTGeneric from rt_generic. Importing * also brings three Literal types which can be useful.
    • all brings in
      • class RTGeneric with class methods tv2type (converts TypeVar to actual type), generic_true (tests if Literal type means "True" or "Yes"), generic_false (tests if Literal type means "No" or "False"), and generic_lit_values (which gives a tuple of all literal values a Literal type represents).
      • TrueT, a Literal type for True.
      • FalseT, a Literal type for False.
      • TypeErrorT, a Literal type RTGeneric functions return when an error occured
      • function has_anyTypeVar(cls) -> bool, which can be used to check if all types in a class have been defined
  2. (Optionally) from rt_generic.type_setup import * for consistent types across python versions
    • For all python versions supported by rt_generic, ensures that these are defined:
      • TypeAlias, GenericAlias, LiteralGenericAlias
      • TypeVar, ClassVar, Generic, Any, Literal, NoneType
      • Self
      • Union, Tuple, Dict, List, TYPE_CHECKING
      • get_args, get_origin, get_original_bases
  3. Declare your generic base class, based on Generic and RTGeneric
    • Either class MyClass[T,U,V](RTGeneric):, (python 3.11 or higher) or
    T = TypeVar('T')
    U = TypeVar('U')
    V = TypeVar('V')
    class MyClass(RTGeneric, Generic[T,U,V]) :
    
  4. Add self.tv2type (or cls.tv2type) calls in your base class where you need behavior to depend on actual types.
    • Just to be clear, self.tv2type(MyClass,T) from an instance method, and cls.tv2type(MyClass,T) from a class method.
    • You must specify your base class name for the cls2 argument to tv2type, since you want to resolve TypeVars specified here. For example, using self.class will attempt to resolve them according to the subclass, where there are no remaining TypeVars.
    • This returns a type, so for example you might have self.tv2type(MyClass, U) == List[int], but not self.tv2type(MyClass, U) == [1,2].
    • You can use Literal types to pass in values when you speciallize your class
      • To turn behavior on and off, use actual types TrueT or FalseT, and check in your code with self.generic_true(MyClass, U)
      • To set values to be used when you speciallize, for example, try using the actual type Literal["Hello World"], and in your code print(f"Custom Message: {self.generic_lit_values(self.tv2type(MyClass,V))[0]}") (note index to select an element from the tuple returned by generic_lit_values).
  5. (Optionally) add a
        def __init_subclass__(cls, **kwargs) :
     	   super().__init_subclass(kwargs)
     	   if not has_anyTypeVars(cls) :
     	   ... # do your own setup, for example instantiate class
     	   ... # variables of types represented by typevars,
     	   ... # now that you know what those types are
    
  6. (Optionally) declare subclasses which partially assign types to generics
    • For example, class MyClass2[T](MyClass[list[T], dict[str,T], Literal["mode2"]]):. Note that in this case tv2type(MyClass2,T) != tv2type(MyClass,T).
  7. Declare subclasses which fully assign types (possibly subclasses of other subclasses).
    • This is required because RTGeneric works by initializing subclasses, and an anonymous subclass (as in a = MyClass[int,int,dict[str, float]]()) does not get initalized.
    • instead do
     class MyFinalClass(MyClass[int,int,dict[str, float]]) :
     	pass
     # or
     class MyFinalClass(MyClass2[str]]) :
     	pass
    
     a = MyFinalClass()
    

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rt_generic-0.2.3.tar.gz (4.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rt_generic-0.2.3-py3-none-any.whl (9.0 kB view details)

Uploaded Python 3

File details

Details for the file rt_generic-0.2.3.tar.gz.

File metadata

  • Download URL: rt_generic-0.2.3.tar.gz
  • Upload date:
  • Size: 4.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.6

File hashes

Hashes for rt_generic-0.2.3.tar.gz
Algorithm Hash digest
SHA256 bc3561034eb8a79b518d31d485e7cf32c0066cc308eab3581bf468db5f4b5490
MD5 cb1370c8131681992e1c4372f0702053
BLAKE2b-256 32453e854a1560d06a2d95113d9804ff8e5fbf874bb50e138096d8e6c8468a97

See more details on using hashes here.

File details

Details for the file rt_generic-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: rt_generic-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 9.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.6

File hashes

Hashes for rt_generic-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 111cd699dcf5004a7b7e478c9963c1164ba903993b17d845b548cc41ee58d19d
MD5 06b259222b7f4f6e9a35b0c6e650bc68
BLAKE2b-256 cb83020fc3b0d7256a448c08bd35a2707859cf4f9510f43c0cf5d150b4011f92

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page