danielgtaylor / danielgtaylor/python-betterproto

Shorter optional fields

Offen
#499 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
enhancement medium
Vorherrschende Sprache
Python
Sterne
1.8k
Forks
234
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

Optional fields generate very long and repetitive code.

The idea is to use shorter and simpler code, and internally consider it as a field having `optional=True, group=f"_{field_name}"`.

Related to #459.

### Proto file
```proto
syntax = "proto3";

message Foo {
optional uint32 bar = 1;
};
```

### Currently generated code
```py
from dataclasses import dataclass

import betterproto

@dataclass(eq=False, repr=False)
class Foo(betterproto.Message):
bar: Optional[int] = betterproto.uint32_field(
1, optional=True, group="_bar"
)
```

### Idea A
Read the type-hint of the field, and if it's a union of a type with `None`, consider it an optional field.

This might be a bad idea if unions of a type + `None` are used for something other than optional fields.

An example implementation of detecting optional fields can be found here: [`cattrs/converters.py`](https://github.com/python-attrs/cattrs/blob/e1db1f24fc884e152f8d19ee02de24d87e97e55a/src/cattrs/converters.py#L109-L110).
```py
from dataclasses import dataclass
from typing import Union, Optional

import betterproto

@dataclass(eq=False, repr=False)
class Foo(betterproto.Message):
bar: Optional[int] = betterproto.uint32_field(1)
```

### Idea B
Use a custom type-hint that indicates a field is optional, and is correctly understood by type-checkers as `Optional`.
```py
from dataclasses import dataclass

import betterproto

@dataclass(eq=False, repr=False)
class Foo(betterproto.Message):
bar: betterproto.OptionalField[int] = betterproto.uint32_field(1)
```

It could possibly be implemented something like this:
[`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated)
```py
from typing import Optional, TypeVar
from typing_extensions import Annotated

T = TypeVar("T")

optional_metadata = object() # or another sentinel

OptionalField = Annotated[Optional[T], optional_metadata]
```

### Idea C
Automatically add `group=f"_{field_name}"` if `optional=True` and no `group` specified.
```py
from dataclasses import dataclass

import betterproto

@dataclass(eq=False, repr=False)
class Foo(betterproto.Message):
bar: Optional[int] = betterproto.uint32_field(1, optional=True)
```

Beitragsleitfaden

Beitragsleitfaden öffnen

Rechercherichtung

Beginne damit, den Generatorpfad nachzuverfolgen, der Python-Felder für optionale proto3-Felder erzeugt; das Issue nennt keine Repository-Dateien oder Tests. Prüfe das referenzierte Beispiel cattrs/converters.py und vergleiche Ideas A, B und C, bevor du dich für einen Ansatz entscheidest. Erledigt ist die Aufgabe, wenn optionale Felder kürzeren Code erzeugen und dabei die korrekte Optional-Typisierung und die Nachverfolgung der Präsenz erhalten bleiben.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python
Bereich
tooling
Issue-Typ
Feature
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Veraltet
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
30/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.