12

I wish to perform static type checking (pylance in vscode) on some dictionaries. The "tricky" part is the I want some of the parameters to be optional and not show up at all in the dictionary. I've tried using dataclasses and TypedDict but without luck so far.

from typing import Optional, List
from dataclasses import dataclass, asdict


@dataclass
class SubOrder:
    name: str


@dataclass
class Order:
    name: str
    sub_orders: Optional[List[SubOrder]]


assert asdict(Order(name="Pizza")) == {"name": "Pizza"}
assert asdict(Order(name="Pizza", sub_orders=[SubOrder(name="Pasta")])) == {
    "name": "Pizza",
    "sub_orders": [{"name": "Pasta"}],
}

Is that achievable with dataclasses? I basically just want my static type checker (pylance / pyright) to check my dictionaries which is why I'm using dataclasses. I've tried with TypedDict as well but the type checkers does not seem to behave like I was. They always require me to set sub_orders.

The following code passes but pylance is not happy about not having sub_orders.

from typing import Optional, List, TypedDict


class SubOrder(TypedDict):
    name: str


class Order(TypedDict):
    name: str
    sub_orders: Optional[List[SubOrder]]


assert Order(name="Pizza") == {"name": "Pizza"}
assert Order(name="Pizza", sub_orders=[SubOrder(name="Pasta")]) == {
    "name": "Pizza",
    "sub_orders": [{"name": "Pasta"}],
}

EDIT

I've added a bug report in pylance since this might actually be a bug in pylance / pyright

mr.bjerre
  • 2,384
  • 2
  • 24
  • 37

2 Answers2

11
from dataclasses import asdict, dataclass
from typing import List, Optional

from validated_dc import ValidatedDC


@dataclass
class SubOrder(ValidatedDC):
    name: str


@dataclass
class Order(ValidatedDC):
    name: str
    sub_orders: Optional[List[SubOrder]] = None

    def as_dict(self):
        data = asdict(self)
        return {key: value for key, value in data.items() if value is not None}


data = {'name': 'pizza'}
order = Order(**data)
assert order.get_errors() is None
assert asdict(order) == {'name': 'pizza', 'sub_orders': None}
assert order.as_dict() == {'name': 'pizza'}

data = {'name': 'pizza', 'sub_orders': [{'name': 'pasta'}]}
order = Order(**data)
assert order.get_errors() is None
assert asdict(order) == {'name': 'pizza', 'sub_orders': [{'name': 'pasta'}]}
assert isinstance(order.sub_orders[0], SubOrder)

ValidatedDC - https://github.com/EvgeniyBurdin/validated_dc

Evgeniy_Burdin
  • 627
  • 5
  • 14
  • Thanks I should’ve mentioned that preferably without any third party libraries - or at least only well established ones. Sorry but thanks anyways! – mr.bjerre Jul 10 '20 at 22:05
  • You only need one file: https://github.com/EvgeniyBurdin/validated_dc/blob/master/validated_dc.py – Evgeniy_Burdin Jul 11 '20 at 08:07
7

You can have an optional parameter in data class by setting it to an empty string like:

from dataclasses import dataclass
@dataclass
class SubOrder:
    name: str=""
Pranu Pranav
  • 353
  • 3
  • 8
  • 3
    Is there a reason why this should be a str specifically? Would it be valid to use None for generic non-str parameters? – John Smith Jun 19 '22 at 09:03