Title: Model serialization drops keys · Issue #105 · gleanwork/api-client-python · GitHub
Open Graph Title: Model serialization drops keys · Issue #105 · gleanwork/api-client-python
X Title: Model serialization drops keys · Issue #105 · gleanwork/api-client-python
Description: There may be an issue affecting the serialize_model methods of the Pydantic models in this library. Taking the DocumentContent model as an example, we see: class DocumentContent(BaseModel): full_text_list: Annotated[ Optional[List[str]],...
Open Graph Description: There may be an issue affecting the serialize_model methods of the Pydantic models in this library. Taking the DocumentContent model as an example, we see: class DocumentContent(BaseModel): full_te...
X Description: There may be an issue affecting the serialize_model methods of the Pydantic models in this library. Taking the DocumentContent model as an example, we see: class DocumentContent(BaseModel): full_te...
Opengraph URL: https://github.com/gleanwork/api-client-python/issues/105
X: @github
Domain: patch-diff.githubusercontent.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Model serialization drops keys","articleBody":"There may be an issue affecting the `serialize_model` methods of the Pydantic models in this library.\n\nTaking the [`DocumentContent`](https://github.com/gleanwork/api-client-python/blob/56b3b86fba975cd22b00cc29c592f0e01143c284/src/glean/api_client/models/documentcontent.py#L16) model as an example, we see:\n\n```py\nclass DocumentContent(BaseModel):\n full_text_list: Annotated[\n Optional[List[str]], pydantic.Field(alias=\"fullTextList\")\n ] = None\n r\"\"\"The plaintext content of the document.\"\"\"\n\n @model_serializer(mode=\"wrap\")\n def serialize_model(self, handler):\n optional_fields = set([\"fullTextList\"])\n serialized = handler(self)\n m = {}\n\n for n, f in type(self).model_fields.items():\n k = f.alias or n\n val = serialized.get(k)\n\n if val != UNSET_SENTINEL:\n if val is not None or k not in optional_fields:\n m[k] = val\n\n return m\n```\n\nThis model uses a field alias that, when constructing the Pydantic object from an API response, will map the `fullTextList` field of the JSON object to the `full_text_list` field of the Pydantic object.\n\nHowever, the model serializer uses:\n\n```py\n...\nk = f.alias or n\nval = serialized.get(k)\n...\n```\n\nwhich means that the field alias (`fullTextList`) will be used to extract the value rather than the Pydantic field name. This results in `value` being `None` and in missing keys in the returned dictionary `m` when the field name and its alias are different.\n\nTo support this claim, please find attached a [`documents.json`](https://github.com/user-attachments/files/24693667/documents.json) file that contains an anonymized response collected from the Glean API (`/rest/api/v1/getdocuments` [endpoint](https://developers.glean.com/api/client-api/documents/getdocuments)).\n\nAnd below is a simple `debug.py` script to run alongside it:\n\n```py\nimport pathlib\n\nfrom glean.api_client import models\nfrom glean.api_client.utils.unmarshal_json_response import unmarshal_json_response\n\n\nclass DummyHttpResponse:\n def __init__(self, text):\n self.status_code = 200\n self.text = text\n\n\nwith pathlib.Path(\"documents.json\").open(\"r\") as f:\n http_res = DummyHttpResponse(\n text=f.read(),\n )\n\n\ndocuments_response = unmarshal_json_response(models.GetDocumentsResponse, http_res)\n\n\nassert isinstance(documents_response, models.GetDocumentsResponse)\nassert documents_response.documents is not None\nassert isinstance(\n documents_response.documents[\"https://company.com/Test\"].content,\n models.DocumentContent,\n)\nassert (\n documents_response.documents[\"https://company.com/Test\"].content.full_text_list[0]\n == \"This is a test document.\"\n)\n\nserialized_document_response = documents_response.model_dump()\n\nassert isinstance(serialized_document_response, dict)\nassert serialized_document_response[\"documents\"] is not None\n\n# Here's the problem: no `full_text_list` or `fullTextList` in the serialized response!\nassert (\n len(serialized_document_response[\"documents\"][\"https://company.com/Test\"][\"content\"])\n \u003e 0\n)\n```\n\nRunning it yields:\n\n```shell\n$ ls \ndebug.py documents.json\n\n$ python debug.py\nTraceback (most recent call last):\n File \"/workspace/app/debug/debug.py\", line 40, in \u003cmodule\u003e\n len(serialized_document_response[\"documents\"][\"https://company.com/Test\"][\"content\"])\n \u003e 0\nAssertionError\n```\n\nNote that I'm using:\n\n```\npydantic_core==2.41.5\npydantic==2.12.5\nglean-api-client==0.11.27\n```","author":{"url":"https://github.com/lucas-bremond","@type":"Person","name":"lucas-bremond"},"datePublished":"2026-01-18T05:36:56.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":1},"url":"https://github.com/105/api-client-python/issues/105"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:b2703d0c-3332-fbcd-2f67-c4e0a388f4cc |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 947C:AB4C6:62A095F:86023BF:697849C7 |
| html-safe-nonce | d71d33696c4bcce3042b2080b234a3d28974679c315d5488e0f6aa311642cbad |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5NDdDOkFCNEM2OjYyQTA5NUY6ODYwMjNCRjo2OTc4NDlDNyIsInZpc2l0b3JfaWQiOiI2NDg2Nzg3NDYzNjY5MzY5Mjg3IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 125d0f51c877a185511e649c97f097a6ed8ff43e61bd0ce27ea92c7cc69587c6 |
| hovercard-subject-tag | issue:3826371445 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/gleanwork/api-client-python/105/issue_layout |
| twitter:image | https://opengraph.githubassets.com/61ad460b6fb3c16fa865926fa2c1cd06a86cf6bdb6906d28add3ad666be2bece/gleanwork/api-client-python/issues/105 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/61ad460b6fb3c16fa865926fa2c1cd06a86cf6bdb6906d28add3ad666be2bece/gleanwork/api-client-python/issues/105 |
| og:image:alt | There may be an issue affecting the serialize_model methods of the Pydantic models in this library. Taking the DocumentContent model as an example, we see: class DocumentContent(BaseModel): full_te... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | lucas-bremond |
| hostname | github.com |
| expected-hostname | github.com |
| None | 2981c597c945c1d90ac6fa355ce7929b2f413dfe7872ca5c435ee53a24a1de50 |
| turbo-cache-control | no-preview |
| go-import | github.com/gleanwork/api-client-python git https://github.com/gleanwork/api-client-python.git |
| octolytics-dimension-user_id | 100331376 |
| octolytics-dimension-user_login | gleanwork |
| octolytics-dimension-repository_id | 971642383 |
| octolytics-dimension-repository_nwo | gleanwork/api-client-python |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 971642383 |
| octolytics-dimension-repository_network_root_nwo | gleanwork/api-client-python |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 520b65a872113b919c1bbdb03834a50af15859fd |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width