From 58562695b16d93d7e4fb2fc6dc4c465baca8adfc Mon Sep 17 00:00:00 2001 From: Zoe <13400804+zoe-icu@users.noreply.github.com> Date: Mon, 6 Jul 2026 18:14:52 +0800 Subject: [PATCH 1/2] fix(apitable.py): guard MetaField property=None and relax pydantic pin - MetaField.__init__ crashed with TypeError when a mapped field type arrived without a 'property' payload (or with property=None), because property_model(**None) is not valid. Guard on isinstance(property, dict) and fall back to data.get('type'). - pyproject.toml: pydantic '2.5.3' -> '^2.5.3' so installs succeed on Python 3.12+/3.14 where 2.5.3 wheels aren't available. - setup.py: pydantic '==1.7' -> '>=2.5.3,<3'; the previous pin was incompatible with the v2 API the code already uses. - Add unit tests covering the property=None regression and nested MagicLookUp property parsing. --- apitable.py/apitable/types/field.py | 7 +++-- apitable.py/pyproject.toml | 2 +- apitable.py/setup.py | 2 +- apitable.py/test/__init__.py | 1 + apitable.py/test/test_meta_field.py | 44 +++++++++++++++++++++++++++++ 5 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 apitable.py/test/test_meta_field.py diff --git a/apitable.py/apitable/types/field.py b/apitable.py/apitable/types/field.py index 48e667e..a47860f 100644 --- a/apitable.py/apitable/types/field.py +++ b/apitable.py/apitable/types/field.py @@ -181,8 +181,11 @@ class MetaField(BaseModel): property: Any = None def __init__(self, property=None, **data) -> None: - property_model = self.get_property_by_type(data['type']) - _property = property_model(**property) if property_model else property + property_model = self.get_property_by_type(data.get('type')) + if property_model and isinstance(property, dict): + _property = property_model(**property) + else: + _property = property super().__init__(property=_property, **data) @staticmethod diff --git a/apitable.py/pyproject.toml b/apitable.py/pyproject.toml index a72a694..ba093de 100644 --- a/apitable.py/pyproject.toml +++ b/apitable.py/pyproject.toml @@ -12,7 +12,7 @@ charset-normalizer = "^3.2.0" environs = "^9.5.0" idna = "^3.4" marshmallow = "^3.20.1" -pydantic = "2.5.3" +pydantic = "^2.5.3" python-dotenv = "^1.0.0" requests = "^2.31.0" urllib3 = "^2.0.4" diff --git a/apitable.py/setup.py b/apitable.py/setup.py index 0aeb053..67ad82e 100644 --- a/apitable.py/setup.py +++ b/apitable.py/setup.py @@ -24,5 +24,5 @@ "Operating System :: OS Independent", ], python_requires=">=3.6", - install_requires=["requests<=2.31.0", "pydantic==1.7", "environs<=9.5.0"], + install_requires=["requests>=2.31.0", "pydantic>=2.5.3,<3", "environs>=9.5.0"], ) \ No newline at end of file diff --git a/apitable.py/test/__init__.py b/apitable.py/test/__init__.py index 58f311a..5db84d4 100644 --- a/apitable.py/test/__init__.py +++ b/apitable.py/test/__init__.py @@ -10,6 +10,7 @@ from .test_get_records import TestGetRecords from .test_get_spaces import TestGetSpaces from .test_get_views import TestGetViews +from .test_meta_field import TestMetaField from .test_unit import TestUnit from .test_update_records import TestUpdateRecords from .test_upload_file import TestUploadFile diff --git a/apitable.py/test/test_meta_field.py b/apitable.py/test/test_meta_field.py new file mode 100644 index 0000000..0a29dd2 --- /dev/null +++ b/apitable.py/test/test_meta_field.py @@ -0,0 +1,44 @@ +import unittest + +from apitable.types.field import MetaField + + +class TestMetaField(unittest.TestCase): + + def test_mapped_type_without_property(self): + field = MetaField(id="fld1", name="n", type="SingleText") + self.assertIsNone(field.property) + + def test_mapped_type_with_property(self): + field = MetaField.model_validate({ + "id": "fld1", + "name": "n", + "type": "SingleText", + "property": {"defaultValue": "x"}, + }) + self.assertEqual(field.property.defaultValue, "x") + + def test_mapped_type_property_none(self): + field = MetaField(id="fld1", name="n", type="SingleText", property=None) + self.assertIsNone(field.property) + + def test_unmapped_type(self): + field = MetaField(id="fld1", name="n", type="URL") + self.assertIsNone(field.property) + + def test_magic_lookup_nested(self): + field = MetaField.model_validate({ + "id": "fld1", + "name": "lookup", + "type": "MagicLookUp", + "property": { + "relatedLinkFieldId": "lf", + "targetFieldId": "tf", + "rollupFunction": "SUM", + }, + }) + self.assertEqual(field.property.rollupFunction.value, "SUM") + + +if __name__ == "__main__": + unittest.main() From d9de60b0cd1a4d4449ff5eb3b1c54f070419d428 Mon Sep 17 00:00:00 2001 From: Zoe <13400804+zoe-icu@users.noreply.github.com> Date: Mon, 6 Jul 2026 19:13:13 +0800 Subject: [PATCH 2/2] ci(py-test): also trigger on pull_request events Fork PRs cannot activate a 'push'-only workflow, so PR #128 shows no CI status. Add pull_request as an additional trigger so PRs against develop get validated too. --- .github/workflows/py-test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/py-test.yml b/.github/workflows/py-test.yml index e644cd3..17ff91a 100644 --- a/.github/workflows/py-test.yml +++ b/.github/workflows/py-test.yml @@ -5,6 +5,10 @@ on: paths: - "apitable.py/**" - ".github/workflows/py-test.yml" + pull_request: + paths: + - "apitable.py/**" + - ".github/workflows/py-test.yml" jobs: unit_test: