From 0d4f8efff29cfc0360fe7e1e71f67801fca8aef0 Mon Sep 17 00:00:00 2001 From: Jonas Lagoni Date: Tue, 7 May 2024 09:17:02 +0200 Subject: [PATCH] fix!: add unique union types for Python (#1982) --- docs/migrations/version-3-to-4.md | 4 +++ src/generators/python/PythonConstrainer.ts | 3 ++- .../python/PythonConstrainer.spec.ts | 25 ++++++++++++++++++- test/runtime/runtime-python.spec.ts | 4 +-- test/runtime/runtime-python/tests/__init__.py | 7 ++++++ .../runtime-python/{test => tests}/main.py | 6 ++--- 6 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 test/runtime/runtime-python/tests/__init__.py rename test/runtime/runtime-python/{test => tests}/main.py (89%) diff --git a/docs/migrations/version-3-to-4.md b/docs/migrations/version-3-to-4.md index 525b445ba1..e21906dd62 100644 --- a/docs/migrations/version-3-to-4.md +++ b/docs/migrations/version-3-to-4.md @@ -95,6 +95,10 @@ Console.WriteLine(dateTime2); Models are aiming to be >= v3.7 compliant. +### Unique types in unions + +In v4, unions types are rendered unique, meaning you will never see `str | str` but just `str`. + ### Pydantic now follows v2 instead of v1 Reference: https://docs.pydantic.dev/2.6/migration/ diff --git a/src/generators/python/PythonConstrainer.ts b/src/generators/python/PythonConstrainer.ts index 1b74ba522b..6d1a872ec6 100644 --- a/src/generators/python/PythonConstrainer.ts +++ b/src/generators/python/PythonConstrainer.ts @@ -50,7 +50,8 @@ export const PythonDefaultTypeMapping: PythonTypeMapping = { const unionTypes = constrainedModel.union.map((unionModel) => { return unionModel.type; }); - return unionTypes.join(' | '); + const uniqueSet = new Set(unionTypes); + return [...uniqueSet].join(' | '); }, Dictionary({ constrainedModel }): string { return `dict[${constrainedModel.key.type}, ${constrainedModel.value.type}]`; diff --git a/test/generators/python/PythonConstrainer.spec.ts b/test/generators/python/PythonConstrainer.spec.ts index a53b0bb9c3..a032f7f322 100644 --- a/test/generators/python/PythonConstrainer.spec.ts +++ b/test/generators/python/PythonConstrainer.spec.ts @@ -199,6 +199,29 @@ describe('PythonConstrainer', () => { expect(type).toEqual('str'); }); test('should render multiple types', () => { + const unionModel1 = new ConstrainedStringModel( + 'test', + undefined, + {}, + 'str' + ); + const unionModel2 = new ConstrainedStringModel( + 'test', + undefined, + {}, + 'str2' + ); + const model = new ConstrainedUnionModel('test', undefined, {}, '', [ + unionModel1, + unionModel2 + ]); + const type = PythonDefaultTypeMapping.Union({ + constrainedModel: model, + ...defaultOptions + }); + expect(type).toEqual('str | str2'); + }); + test('should render unique types', () => { const unionModel1 = new ConstrainedStringModel( 'test', undefined, @@ -219,7 +242,7 @@ describe('PythonConstrainer', () => { constrainedModel: model, ...defaultOptions }); - expect(type).toEqual('str | str'); + expect(type).toEqual('str'); }); }); diff --git a/test/runtime/runtime-python.spec.ts b/test/runtime/runtime-python.spec.ts index a290ab32de..f23a4bc22a 100644 --- a/test/runtime/runtime-python.spec.ts +++ b/test/runtime/runtime-python.spec.ts @@ -9,8 +9,8 @@ test("Python runtime testing", async () => { const compileCommand = `cd ${path.resolve( __dirname, - "./runtime-python/test/" - )} && python3 main.py`; + "./runtime-python" + )} && python3 -m unittest discover ./tests`; await execCommand(compileCommand,true); }); \ No newline at end of file diff --git a/test/runtime/runtime-python/tests/__init__.py b/test/runtime/runtime-python/tests/__init__.py new file mode 100644 index 0000000000..51b46a879f --- /dev/null +++ b/test/runtime/runtime-python/tests/__init__.py @@ -0,0 +1,7 @@ +import os +import sys +PROJECT_PATH = os.getcwd() +SOURCE_PATH = os.path.join( + PROJECT_PATH, "src" +) +sys.path.append(SOURCE_PATH) \ No newline at end of file diff --git a/test/runtime/runtime-python/test/main.py b/test/runtime/runtime-python/tests/main.py similarity index 89% rename from test/runtime/runtime-python/test/main.py rename to test/runtime/runtime-python/tests/main.py index fb98a0a351..49121eeb80 100644 --- a/test/runtime/runtime-python/test/main.py +++ b/test/runtime/runtime-python/tests/main.py @@ -1,8 +1,6 @@ import unittest -import sys -sys.path.insert(1, '../src/main/') -from Address import Address -from NestedObject import NestedObject +from src.main.Address import Address +from src.main.NestedObject import NestedObject import json class TestAddress(unittest.TestCase):