auto-pi-lot / autopilot

Distributed behavioral experiments
https://docs.auto-pi-lot.com
Mozilla Public License 2.0
93 stars 24 forks source link

Change prefs to `pydantic` #155

Open sneakers-the-rat opened 2 years ago

sneakers-the-rat commented 2 years ago

Trying pydantic right now and absolutely love it. Seems like a superset of dataclasses that makes typing very easy.

For example:

from pydantic import BaseModel, Field
from typing import Optional
from pathlib import Path

class Directories(BaseModel):
    user_dir: Path = Path.home() / 'autopilot'
    prefs_file: Path = user_dir / 'prefs.json'

class Audio_Prefs(BaseModel):
    fs:int = 192000
    device: str = Field(description="The sound device to use!")

class Pigpio_Prefs(BaseModel):
    mask: str = 'etc.'

class Prefs(BaseModel):
    audio_prefs: Audio_Prefs
    pigpio_prefs: Optional[Pigpio_Prefs] = None

    def save(self, file: Path = Directories.prefs_file):
        with open(file, 'w') as pfile:
            pfile.write(self.json())

    @classmethod
    def load(cls, file: Path = Directories.prefs_file) -> 'Prefs':
        with open(file, 'r') as pfile:
            prefs_raw = pfile.read()
        return Prefs.parse_raw(prefs_raw)

This would solve a lot of problems, like the ballooning complexity of the prefs file and its relatively undocumented state, the ability to have impossible values which causes a lot of coercion throughout the program, and the awkwardness of the current multiprocessing-safe prefs which could be solved by writing/reading to a file with a lock.

sneakers-the-rat commented 2 years ago

We could also make a trivial mapping from types to graphical elements and solve this and make the prefs TUI a lot a lot a lot easier to manage, re: https://github.com/wehr-lab/autopilot/issues/153

sneakers-the-rat commented 2 years ago

ope https://pydantic-docs.helpmanual.io/usage/settings/

cxrodgers commented 2 years ago

One gotcha I've run into before when hardcoding my own prefs-esque code is things like "True" vs True, or "1" vs 1, or "None" vs None vs np.nan vs "" vs, and you want something to handle that sanely when converting to/from plaintext. I imagine pydantic might be good for that

sneakers-the-rat commented 2 years ago

One gotcha I've run into before when hardcoding my own prefs-esque code is things like "True" vs True, or "1" vs 1, or "None" vs None vs np.nan vs "" vs, and you want something to handle that sanely when converting to/from plaintext. I imagine pydantic might be good for that

Definitely. and the current prefs manager is a really huge mess for any circumstance where you're using autopilot stuff in a nonstandard context due to the use of the multiprocessing manager... Definitely need to move to a different model of setting prefs where it's not globally shared across the module but each module knows how to get its own. Prefs are basically never prefs.set anyway, and the times they are are just sort of a stopgap that should be resolved.