Python TypedDict 'a' or 'b' but not both in inherited TypedDict's

125 views Asked by At

I have some data coming in that could look like

{"cloud_url": "https://example.com/file.txt", "filetype": "txt"}

or it could look like

{"local_filepath": "./file.csv", "filetype": "csv", "delimeter": ","}

for my typing I currently have

from typing import Literal, TypedDict

class _FileLocal(TypedDict):
    local_filepath: str

class _FileCloud(TypedDict):
    cloud_url: str

_FileCloudOrLocal = _FileLocal | _FileCloud

class _FileTextProcess(_FileCloudOrLocal):
    filetype: Literal['txt']

class _FileCSVProcess(_FileCloudOrLocal):
    filetype: Literal['csv']
    delimeter: str

FileProcess = _FileTextProcess | _FileCSVProcess

the issue with this version is that _FileTextProcess can't inherit from a union (a class has to inherit from a class)

how can I specify that local_filepath or cloud_url but not both must be supplied

2

There are 2 answers

2
codythecoder On

my current workaround is to create a class for each possible combination, but obviously this isn't feasible for anything much more complex than this

from typing import Literal, TypedDict


class _FileLocal(TypedDict):
    local_filepath: str

class _FileCloud(TypedDict):
    cloud_url: str

class _FileTextProcess(TypedDict):
    filetype: Literal['txt']

class _FileCSVProcess(TypedDict):
    filetype: Literal['csv']
    delimeter: str

class A(_FileLocal, _FileTextProcess): pass
class B(_FileLocal, _FileCSVProcess): pass
class C(_FileCloud, _FileTextProcess): pass
class D(_FileCloud, _FileCSVProcess): pass

FileProcess = A | B | C | D
2
leaf_yakitori On
  • just use Multiple Inheritance
  • in python Multiple Inheritance should be
class MultiDerived(SuperClass1, SuperClass2):

Not |

  • I make it feasible so that you can apply any 2 parent class to it.

  • example code:

from typing import Literal, TypedDict
import itertools

class _FileLocal(TypedDict):
    local_filepath: str

class _FileCloud(TypedDict):
    cloud_url: str

class _FileTextProcess(TypedDict):
    filetype: Literal['txt']

class _FileCSVProcess(TypedDict):
    filetype: Literal['csv']
    delimeter: str

def create_process(base1,base2):
    class MyClass(base1,base2):
        pass
    return MyClass

FileProcess = None
for element in itertools.product([_FileLocal,_FileCloud], [_FileTextProcess, _FileCSVProcess]):
    FileProcess = create_process(*element) if FileProcess == None else FileProcess | create_process(*element)