Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python: anonymous record creation generates camel case dict fields #3870

Open
joprice opened this issue Jul 22, 2024 · 4 comments
Open

python: anonymous record creation generates camel case dict fields #3870

joprice opened this issue Jul 22, 2024 · 4 comments

Comments

@joprice
Copy link
Contributor

joprice commented Jul 22, 2024

Description

When using the python backend and making use of an anonymous record, the accessors use snake case, while the construction code has camel cased fields, resulting in a KeyError when called:

Repro code

type data = {| fromId: int |}

// this creates a dict with camel case field "fromId"
let makeData() = {| fromId = 1 |}

let y (d: data) = 
  d.fromId
from typing import Any

def make_data(__unit: None=None) -> dict[str, Any]:
    return {
        "fromId": 1
    }


def y(d: dict[str, Any]) -> int:
    return d["from_id"]

https://fable.io/repl/#?code=FAFwngDgpgBAJgQxAmBeGBvAPjAZgJwHsBbASTgC4YBLAOxBiwF9hgB6NmEAC2oGcYAY3xQkUASjjVBDAO7UeQhMSgAbJX1i5qauDABEBEuX3BVUBsQQBrKABEkCABQBKNJhxGye9AEZGLGYWMGAwTpTwjm7owDDwAHRe5EA&html=Q&css=Q

Related information

  • Fable version: 4.19.3
  • Operating system: osx
@joprice
Copy link
Contributor Author

joprice commented Jul 22, 2024

Something else to note: when the field starts with a capital, it is left unchanged:

type data = {| FromId: int |}

// this creates a dict with camel case field "fromId"
let makeData() = {| FromId = 1 |}

let y (d: data) = 
  d.FromId
from typing import Any

def make_data(__unit: None=None) -> dict[str, Any]:
    return {
        "FromId": 1
    }


def y(d: dict[str, Any]) -> int:
    return d["FromId"]

@MangelMaxime
Copy link
Member

A similar problem happens when using Record.

type Test =
    {
        FirstName : string  
        lastName : string
    }

let test =
    {
        FirstName = ""
        lastName = ""
    }

test.FirstName
test.lastName
from __future__ import annotations
from dataclasses import dataclass
from fable_library_js.reflection import (TypeInfo, string_type, record_type)
from fable_library_js.types import Record

def _expr0() -> TypeInfo:
    return record_type("Test.Test", [], Test, lambda: [("FirstName", string_type), ("last_name", string_type)])


@dataclass(eq = False, repr = False, slots = True)
class Test(Record):
    FirstName: str
    last_name: str

Test_reflection = _expr0

test: Test = Test("", "")

test.FirstName

test.last_name

I suspect that the convention in Python is to use snake_case? @dbrattli

@joprice
Copy link
Contributor Author

joprice commented Jul 22, 2024

As far as I can understand, the record one seems to be working, in the sense that the class contains the same fields as the accessor used at the call-site: FirstName:str vs test.FirstName. Whereas with the anonymous one, the two do not line up: the creation side uses camel case {"fromId": 1 } and the accessor uses snake case d["from_id"].

So it seems there's a convention in the python backend of converting to snake case automatically, but it's not implemented correctly for anonymous records.

@MangelMaxime
Copy link
Member

Yes, but it also seems like the snake_case conversion is only if you use camelCase.

I don't know if this should also be applied to properties that use PascalCase.

However, if we do so there is a need to mangle the name of the property because FirstName and firstName would both be translated to first_name. Perhaps, this is the reason why only camelCase are transformed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants