diff --git a/pyproject.toml b/pyproject.toml index 83ea69dc6..b0eb19511 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,6 +48,7 @@ cli = [ "docformatter>=1.7.2", "jinja2>=2.10", "toposort>=1.5", + "ruff>=0.1.9" ] docs = [ "furo", diff --git a/tests/codegen/test_writer.py b/tests/codegen/test_writer.py index 5f5fe5987..e532740af 100644 --- a/tests/codegen/test_writer.py +++ b/tests/codegen/test_writer.py @@ -23,11 +23,15 @@ def setUp(self): generator = NoneGenerator(config) self.writer = CodeWriter(generator) + @mock.patch.object(CodeWriter, "ruff_code") @mock.patch.object(NoneGenerator, "render_header") @mock.patch.object(NoneGenerator, "render") @mock.patch.object(NoneGenerator, "normalize_packages") - def test_write(self, mock_normalize_packages, mock_render, mock_render_header): + def test_write( + self, mock_normalize_packages, mock_render, mock_render_header, mock_ruff_code + ): classes = ClassFactory.list(2) + mock_ruff_code.side_effect = lambda x, y: x with TemporaryDirectory() as tmpdir: mock_render.return_value = [ GeneratorResult(Path(f"{tmpdir}/foo/a.py"), "file", "aAa"), @@ -42,21 +46,33 @@ def test_write(self, mock_normalize_packages, mock_render, mock_render_header): self.assertFalse(Path(f"{tmpdir}/c.py").exists()) mock_normalize_packages.assert_called_once_with(classes) + @mock.patch.object(CodeWriter, "ruff_code") + @mock.patch.object(NoneGenerator, "render_header") @mock.patch.object(NoneGenerator, "render") @mock.patch.object(NoneGenerator, "normalize_packages") @mock.patch("builtins.print") - def test_print(self, mock_print, mock_normalize_packages, mock_render): + def test_print( + self, + mock_print, + mock_normalize_packages, + mock_render, + mock_render_header, + mock_ruff_code, + ): classes = ClassFactory.list(2) mock_render.return_value = [ GeneratorResult(Path("foo/a.py"), "file", "aAa"), GeneratorResult(Path("bar/b.py"), "file", "bBb"), GeneratorResult(Path("c.py"), "file", ""), ] + mock_render_header.return_value = "# H\n" + mock_ruff_code.side_effect = lambda x, y: x + self.writer.print(classes) mock_normalize_packages.assert_called_once_with(classes) mock_print.assert_has_calls( - [mock.call("aAa", end=""), mock.call("bBb", end="")] + [mock.call("# H\naAa", end=""), mock.call("# H\nbBb", end="")] ) def test_from_config(self): @@ -71,3 +87,42 @@ def test_from_config(self): CodeWriter.register_generator("dataclasses", DataclassGenerator) writer = CodeWriter.from_config(config) self.assertIsInstance(writer.generator, DataclassGenerator) + + def test_ruff_code(self): + src_code = ( + "\n" + "import sys\n" + "@dataclass\n" + "\n" + "class MyType:\n" + "\n" + ' value: Optional[str] = field(default=None, metadata={"type": "Element", "required": True})\n' + "\n" + "\n" + " " + ) + + self.writer.generator.config.output.max_line_length = 55 + actual = self.writer.ruff_code(src_code, Path(__file__)) + expected = ( + "import sys\n" + "\n" + "\n" + "@dataclass\n" + "class MyType:\n" + " value: Optional[str] = field(\n" + " default=None,\n" + ' metadata={"type": "Element", "required": True},\n' + " )\n" + ) + self.assertEqual(expected, actual) + + def test_format_with_invalid_code(self): + src_code = """a = "1""" + file_path = Path(__file__) + + self.writer.generator.config.output.max_line_length = 55 + with self.assertRaises(CodeGenerationError) as cm: + self.writer.ruff_code(src_code, file_path) + + self.assertIn("Ruff failed", str(cm.exception)) diff --git a/tests/fixtures/annotations/model.py b/tests/fixtures/annotations/model.py index 04df65a2c..b0f602b99 100644 --- a/tests/fixtures/annotations/model.py +++ b/tests/fixtures/annotations/model.py @@ -1,5 +1,6 @@ from __future__ import annotations from dataclasses import dataclass, field +from typing import Optional from tests.fixtures.annotations.units import unit __NAMESPACE__ = "http://domain.org/schema/model" @@ -7,17 +8,17 @@ @dataclass class Measurement: - value: float | None = field( + value: Optional[float] = field( default=None, metadata={ "required": True, - } + }, ) - unit: unit | None = field( + unit: Optional[unit] = field( default=None, metadata={ "type": "Attribute", - } + }, ) diff --git a/tests/fixtures/annotations/units.py b/tests/fixtures/annotations/units.py index 24fdf89f0..587edcb73 100644 --- a/tests/fixtures/annotations/units.py +++ b/tests/fixtures/annotations/units.py @@ -5,7 +5,7 @@ class unit(Enum): - M = 'm' - KG = 'kg' - VALUE = '%' - NA = 'NA' + M = "m" + KG = "kg" + VALUE = "%" + NA = "NA" diff --git a/tests/fixtures/artists/metadata.py b/tests/fixtures/artists/metadata.py index 01b9ad24d..b2e4eecd2 100644 --- a/tests/fixtures/artists/metadata.py +++ b/tests/fixtures/artists/metadata.py @@ -15,7 +15,7 @@ class Meta: default=None, metadata={ "type": "Attribute", - } + }, ) sort_name: Optional[str] = field( default=None, @@ -23,33 +23,33 @@ class Meta: "name": "sort-name", "type": "Attribute", "required": True, - } + }, ) type_value: Optional[str] = field( default=None, metadata={ "name": "type", "type": "Attribute", - } + }, ) type_id: Optional[str] = field( default=None, metadata={ "name": "type-id", "type": "Attribute", - } + }, ) primary: Optional[str] = field( default=None, metadata={ "type": "Attribute", - } + }, ) value: str = field( - default='', + default="", metadata={ "required": True, - } + }, ) @@ -64,14 +64,14 @@ class Meta: metadata={ "type": "Attribute", "required": True, - } + }, ) name: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) sort_name: Optional[str] = field( default=None, @@ -79,7 +79,7 @@ class Meta: "name": "sort-name", "type": "Element", "required": True, - } + }, ) @@ -94,13 +94,13 @@ class Meta: metadata={ "type": "Attribute", "required": True, - } + }, ) value: str = field( - default='', + default="", metadata={ "required": True, - } + }, ) @@ -115,7 +115,7 @@ class Meta: metadata={ "type": "Element", "min_occurs": 1, - } + }, ) @@ -130,7 +130,7 @@ class Meta: metadata={ "type": "Element", "min_occurs": 1, - } + }, ) @@ -146,7 +146,7 @@ class Meta: "name": "iso-3166-1-code", "type": "Element", "required": True, - } + }, ) @@ -162,7 +162,7 @@ class Meta: "name": "iso-3166-2-code", "type": "Element", "required": True, - } + }, ) @@ -177,19 +177,19 @@ class Meta: metadata={ "type": "Element", "required": True, - } + }, ) end: Optional[XmlDate] = field( default=None, metadata={ "type": "Element", - } + }, ) ended: Optional[bool] = field( default=None, metadata={ "type": "Element", - } + }, ) @@ -204,14 +204,14 @@ class Meta: metadata={ "type": "Attribute", "required": True, - } + }, ) alias: List[Alias] = field( default_factory=list, metadata={ "type": "Element", "min_occurs": 1, - } + }, ) @@ -226,14 +226,14 @@ class Meta: metadata={ "type": "Attribute", "required": True, - } + }, ) name: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) sort_name: Optional[str] = field( default=None, @@ -241,7 +241,7 @@ class Meta: "name": "sort-name", "type": "Element", "required": True, - } + }, ) iso_3166_1_code_list: Optional[Iso31661CodeList] = field( default=None, @@ -249,7 +249,7 @@ class Meta: "name": "iso-3166-1-code-list", "type": "Element", "required": True, - } + }, ) @@ -264,14 +264,14 @@ class Meta: metadata={ "type": "Attribute", "required": True, - } + }, ) name: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) sort_name: Optional[str] = field( default=None, @@ -279,7 +279,7 @@ class Meta: "name": "sort-name", "type": "Element", "required": True, - } + }, ) iso_3166_2_code_list: Optional[Iso31662CodeList] = field( default=None, @@ -287,7 +287,7 @@ class Meta: "name": "iso-3166-2-code-list", "type": "Element", "required": True, - } + }, ) @@ -302,7 +302,7 @@ class Meta: metadata={ "type": "Attribute", "required": True, - } + }, ) type_value: Optional[str] = field( default=None, @@ -310,7 +310,7 @@ class Meta: "name": "type", "type": "Attribute", "required": True, - } + }, ) type_id: Optional[str] = field( default=None, @@ -318,14 +318,14 @@ class Meta: "name": "type-id", "type": "Attribute", "required": True, - } + }, ) name: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) sort_name: Optional[str] = field( default=None, @@ -333,26 +333,26 @@ class Meta: "name": "sort-name", "type": "Element", "required": True, - } + }, ) disambiguation: Optional[str] = field( default=None, metadata={ "type": "Element", - } + }, ) ipi: Optional[str] = field( default=None, metadata={ "type": "Element", - } + }, ) ipi_list: Optional[IpiList] = field( default=None, metadata={ "name": "ipi-list", "type": "Element", - } + }, ) isni_list: Optional[IsniList] = field( default=None, @@ -360,27 +360,27 @@ class Meta: "name": "isni-list", "type": "Element", "required": True, - } + }, ) gender: Optional[Gender] = field( default=None, metadata={ "type": "Element", - } + }, ) country: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) area: Optional[Area] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) begin_area: Optional[BeginArea] = field( default=None, @@ -388,14 +388,14 @@ class Meta: "name": "begin-area", "type": "Element", "required": True, - } + }, ) end_area: Optional[EndArea] = field( default=None, metadata={ "name": "end-area", "type": "Element", - } + }, ) life_span: Optional[LifeSpan] = field( default=None, @@ -403,7 +403,7 @@ class Meta: "name": "life-span", "type": "Element", "required": True, - } + }, ) alias_list: Optional[AliasList] = field( default=None, @@ -411,7 +411,7 @@ class Meta: "name": "alias-list", "type": "Element", "required": True, - } + }, ) @@ -426,5 +426,5 @@ class Meta: metadata={ "type": "Element", "required": True, - } + }, ) diff --git a/tests/fixtures/books/books.py b/tests/fixtures/books/books.py index 04132ae68..ffdbc4d7e 100644 --- a/tests/fixtures/books/books.py +++ b/tests/fixtures/books/books.py @@ -20,13 +20,14 @@ class BookForm: id: International Standard Book Number lang: Language ISO Code """ + author: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", "required": True, - } + }, ) title: Optional[str] = field( default=None, @@ -34,7 +35,7 @@ class BookForm: "type": "Element", "namespace": "", "required": True, - } + }, ) genre: Optional[str] = field( default=None, @@ -42,7 +43,7 @@ class BookForm: "type": "Element", "namespace": "", "required": True, - } + }, ) price: Optional[float] = field( default=None, @@ -50,7 +51,7 @@ class BookForm: "type": "Element", "namespace": "", "required": True, - } + }, ) pub_date: Optional[XmlDate] = field( default=None, @@ -58,7 +59,7 @@ class BookForm: "type": "Element", "namespace": "", "required": True, - } + }, ) review: Optional[str] = field( default=None, @@ -66,20 +67,20 @@ class BookForm: "type": "Element", "namespace": "", "required": True, - } + }, ) id: Optional[str] = field( default=None, metadata={ "type": "Attribute", - } + }, ) lang: str = field( init=False, - default='en', + default="en", metadata={ "type": "Attribute", - } + }, ) @@ -90,7 +91,7 @@ class BooksForm: metadata={ "type": "Element", "namespace": "", - } + }, ) @@ -99,6 +100,7 @@ class Books(BooksForm): """ Το βιβλίο. """ + class Meta: name = "books" namespace = "urn:books" diff --git a/tests/fixtures/calculator/services.py b/tests/fixtures/calculator/services.py index 68b5efb3f..5ef90dd4c 100644 --- a/tests/fixtures/calculator/services.py +++ b/tests/fixtures/calculator/services.py @@ -15,7 +15,7 @@ class Meta: "name": "intA", "type": "Element", "required": True, - } + }, ) int_b: Optional[int] = field( default=None, @@ -23,7 +23,7 @@ class Meta: "name": "intB", "type": "Element", "required": True, - } + }, ) @@ -38,7 +38,7 @@ class Meta: "name": "AddResult", "type": "Element", "required": True, - } + }, ) @@ -53,7 +53,7 @@ class Meta: "name": "intA", "type": "Element", "required": True, - } + }, ) int_b: Optional[int] = field( default=None, @@ -61,7 +61,7 @@ class Meta: "name": "intB", "type": "Element", "required": True, - } + }, ) @@ -76,7 +76,7 @@ class Meta: "name": "DivideResult", "type": "Element", "required": True, - } + }, ) @@ -91,7 +91,7 @@ class Meta: "name": "intA", "type": "Element", "required": True, - } + }, ) int_b: Optional[int] = field( default=None, @@ -99,7 +99,7 @@ class Meta: "name": "intB", "type": "Element", "required": True, - } + }, ) @@ -114,7 +114,7 @@ class Meta: "name": "MultiplyResult", "type": "Element", "required": True, - } + }, ) @@ -129,7 +129,7 @@ class Meta: "name": "intA", "type": "Element", "required": True, - } + }, ) int_b: Optional[int] = field( default=None, @@ -137,7 +137,7 @@ class Meta: "name": "intB", "type": "Element", "required": True, - } + }, ) @@ -152,7 +152,7 @@ class Meta: "name": "SubtractResult", "type": "Element", "required": True, - } + }, ) @@ -167,7 +167,7 @@ class Meta: metadata={ "name": "Body", "type": "Element", - } + }, ) @dataclass @@ -178,7 +178,7 @@ class Body: "name": "Add", "type": "Element", "namespace": "http://tempuri.org/", - } + }, ) @@ -193,7 +193,7 @@ class Meta: metadata={ "name": "Body", "type": "Element", - } + }, ) @dataclass @@ -204,14 +204,14 @@ class Body: "name": "AddResponse", "type": "Element", "namespace": "http://tempuri.org/", - } + }, ) fault: Optional["CalculatorSoapAddOutput.Body.Fault"] = field( default=None, metadata={ "name": "Fault", "type": "Element", - } + }, ) @dataclass @@ -221,28 +221,28 @@ class Fault: metadata={ "type": "Element", "namespace": "", - } + }, ) faultstring: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", - } + }, ) faultactor: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", - } + }, ) detail: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", - } + }, ) @@ -257,7 +257,7 @@ class Meta: metadata={ "name": "Body", "type": "Element", - } + }, ) @dataclass @@ -268,7 +268,7 @@ class Body: "name": "Divide", "type": "Element", "namespace": "http://tempuri.org/", - } + }, ) @@ -283,7 +283,7 @@ class Meta: metadata={ "name": "Body", "type": "Element", - } + }, ) @dataclass @@ -294,14 +294,14 @@ class Body: "name": "DivideResponse", "type": "Element", "namespace": "http://tempuri.org/", - } + }, ) fault: Optional["CalculatorSoapDivideOutput.Body.Fault"] = field( default=None, metadata={ "name": "Fault", "type": "Element", - } + }, ) @dataclass @@ -311,28 +311,28 @@ class Fault: metadata={ "type": "Element", "namespace": "", - } + }, ) faultstring: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", - } + }, ) faultactor: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", - } + }, ) detail: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", - } + }, ) @@ -347,7 +347,7 @@ class Meta: metadata={ "name": "Body", "type": "Element", - } + }, ) @dataclass @@ -358,7 +358,7 @@ class Body: "name": "Multiply", "type": "Element", "namespace": "http://tempuri.org/", - } + }, ) @@ -373,7 +373,7 @@ class Meta: metadata={ "name": "Body", "type": "Element", - } + }, ) @dataclass @@ -384,14 +384,14 @@ class Body: "name": "MultiplyResponse", "type": "Element", "namespace": "http://tempuri.org/", - } + }, ) fault: Optional["CalculatorSoapMultiplyOutput.Body.Fault"] = field( default=None, metadata={ "name": "Fault", "type": "Element", - } + }, ) @dataclass @@ -401,28 +401,28 @@ class Fault: metadata={ "type": "Element", "namespace": "", - } + }, ) faultstring: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", - } + }, ) faultactor: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", - } + }, ) detail: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", - } + }, ) @@ -437,7 +437,7 @@ class Meta: metadata={ "name": "Body", "type": "Element", - } + }, ) @dataclass @@ -448,7 +448,7 @@ class Body: "name": "Subtract", "type": "Element", "namespace": "http://tempuri.org/", - } + }, ) @@ -463,7 +463,7 @@ class Meta: metadata={ "name": "Body", "type": "Element", - } + }, ) @dataclass @@ -474,14 +474,14 @@ class Body: "name": "SubtractResponse", "type": "Element", "namespace": "http://tempuri.org/", - } + }, ) fault: Optional["CalculatorSoapSubtractOutput.Body.Fault"] = field( default=None, metadata={ "name": "Fault", "type": "Element", - } + }, ) @dataclass @@ -491,28 +491,28 @@ class Fault: metadata={ "type": "Element", "namespace": "", - } + }, ) faultstring: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", - } + }, ) faultactor: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", - } + }, ) detail: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", - } + }, ) diff --git a/tests/fixtures/compound/models.py b/tests/fixtures/compound/models.py index 04de69e4b..56cdc59ac 100644 --- a/tests/fixtures/compound/models.py +++ b/tests/fixtures/compound/models.py @@ -12,7 +12,7 @@ class Meta: default=True, metadata={ "type": "Attribute", - } + }, ) @@ -26,7 +26,7 @@ class Meta: default=True, metadata={ "type": "Attribute", - } + }, ) @@ -49,5 +49,5 @@ class Meta: "type": Bravo, }, ), - } + }, ) diff --git a/tests/fixtures/docstrings/accessible/schema.py b/tests/fixtures/docstrings/accessible/schema.py index bc4a82bd6..de303622d 100644 --- a/tests/fixtures/docstrings/accessible/schema.py +++ b/tests/fixtures/docstrings/accessible/schema.py @@ -11,6 +11,7 @@ class DoubleQuotesDescription: Dont trip on quotes: "A", "B", "C", "D" My\\Ipsum """ + class Meta: namespace = "urn:docs" @@ -18,13 +19,14 @@ class Meta: @dataclass class DoubleQuotesSummary: """Dont trip on quotes: "A", "B", "C", "D" My\\Ipsum""" + class Meta: namespace = "urn:docs" class RootEnum(Enum): - A = 'A' - B = 'B' + A = "A" + B = "B" RootEnum.A.__doc__ = "Lorem ipsum dolor" @@ -35,8 +37,8 @@ class RootEnum(Enum): class RootB(Enum): - YES = 'Yes' - NO = 'No' + YES = "Yes" + NO = "No" RootB.YES.__doc__ = ( @@ -47,8 +49,8 @@ class RootB(Enum): class RootD(Enum): - TRUE = 'true' - FALSE = 'false' + TRUE = "true" + FALSE = "false" @dataclass @@ -60,6 +62,7 @@ class Root: Donec imperdiet lacus sed sagittis scelerisque. Ut sodales metus: "sit", "amet", "lectus" My\\Ipsum """ + class Meta: namespace = "urn:docs" @@ -74,7 +77,7 @@ class Meta: " dolor sit amet, consectetur adipiscing elit. Aliquam " "nec.\nMy\\Ipsum" ), - } + }, ) b: Optional[RootB] = field( default=None, @@ -83,7 +86,7 @@ class Meta: "namespace": "", "required": True, "doc": "This is a second root type field documentation.", - } + }, ) c: Optional[RootEnum] = field( default=None, @@ -91,7 +94,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) d: Optional[RootD] = field( default=None, @@ -99,7 +102,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) @dataclass @@ -107,6 +110,7 @@ class A: """ This is an inner type documentation. """ + sub_a: Optional[str] = field( default=None, metadata={ @@ -118,5 +122,5 @@ class A: " dolor sit amet, consectetur adipiscing elit. Vivamus " "efficitur.\nMy\\Ipsum" ), - } + }, ) diff --git a/tests/fixtures/docstrings/blank/schema.py b/tests/fixtures/docstrings/blank/schema.py index 1311ed055..223982f27 100644 --- a/tests/fixtures/docstrings/blank/schema.py +++ b/tests/fixtures/docstrings/blank/schema.py @@ -18,18 +18,18 @@ class Meta: class RootEnum(Enum): - A = 'A' - B = 'B' + A = "A" + B = "B" class RootB(Enum): - YES = 'Yes' - NO = 'No' + YES = "Yes" + NO = "No" class RootD(Enum): - TRUE = 'true' - FALSE = 'false' + TRUE = "true" + FALSE = "false" @dataclass @@ -43,7 +43,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) b: Optional[RootB] = field( default=None, @@ -51,7 +51,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) c: Optional[RootEnum] = field( default=None, @@ -59,7 +59,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) d: Optional[RootD] = field( default=None, @@ -67,7 +67,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) @dataclass @@ -78,5 +78,5 @@ class A: "type": "Element", "namespace": "", "required": True, - } + }, ) diff --git a/tests/fixtures/docstrings/google/schema.py b/tests/fixtures/docstrings/google/schema.py index aebe68ace..9eacc6049 100644 --- a/tests/fixtures/docstrings/google/schema.py +++ b/tests/fixtures/docstrings/google/schema.py @@ -11,6 +11,7 @@ class DoubleQuotesDescription: Dont trip on quotes: "A", "B", "C", "D" My\\Ipsum """ + class Meta: namespace = "urn:docs" @@ -18,6 +19,7 @@ class Meta: @dataclass class DoubleQuotesSummary: """Dont trip on quotes: "A", "B", "C", "D" My\\Ipsum""" + class Meta: namespace = "urn:docs" @@ -29,8 +31,9 @@ class RootEnum(Enum): B: Lorem ipsum dolor '''sit''' amet, consectetur adipiscing elit. Morbi dapibus. My\\Ipsum """ - A = 'A' - B = 'B' + + A = "A" + B = "B" class RootB(Enum): @@ -40,13 +43,14 @@ class RootB(Enum): dolor sit amet, consectetur adipiscing elit. Etiam mollis. NO: Lorem ipsum dolor My\\Ipsum """ - YES = 'Yes' - NO = 'No' + + YES = "Yes" + NO = "No" class RootD(Enum): - TRUE = 'true' - FALSE = 'false' + TRUE = "true" + FALSE = "false" @dataclass @@ -66,6 +70,7 @@ class Root: c: d: """ + class Meta: namespace = "urn:docs" @@ -75,7 +80,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) b: Optional[RootB] = field( default=None, @@ -83,7 +88,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) c: Optional[RootEnum] = field( default=None, @@ -91,7 +96,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) d: Optional[RootD] = field( default=None, @@ -99,7 +104,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) @dataclass @@ -112,11 +117,12 @@ class A: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus efficitur. My\\Ipsum """ + sub_a: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", "required": True, - } + }, ) diff --git a/tests/fixtures/docstrings/numpy/schema.py b/tests/fixtures/docstrings/numpy/schema.py index 54c3bb522..b63512d54 100644 --- a/tests/fixtures/docstrings/numpy/schema.py +++ b/tests/fixtures/docstrings/numpy/schema.py @@ -11,6 +11,7 @@ class DoubleQuotesDescription: Dont trip on quotes: "A", "B", "C", "D" My\\Ipsum """ + class Meta: namespace = "urn:docs" @@ -18,6 +19,7 @@ class Meta: @dataclass class DoubleQuotesSummary: """Dont trip on quotes: "A", "B", "C", "D" My\\Ipsum""" + class Meta: namespace = "urn:docs" @@ -32,8 +34,9 @@ class RootEnum(Enum): Lorem ipsum dolor '''sit''' amet, consectetur adipiscing elit. Morbi dapibus. My\\Ipsum """ - A = 'A' - B = 'B' + + A = "A" + B = "B" class RootB(Enum): @@ -46,13 +49,14 @@ class RootB(Enum): NO Lorem ipsum dolor My\\Ipsum """ - YES = 'Yes' - NO = 'No' + + YES = "Yes" + NO = "No" class RootD(Enum): - TRUE = 'true' - FALSE = 'false' + TRUE = "true" + FALSE = "false" @dataclass @@ -74,6 +78,7 @@ class Root: c d """ + class Meta: namespace = "urn:docs" @@ -83,7 +88,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) b: Optional[RootB] = field( default=None, @@ -91,7 +96,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) c: Optional[RootEnum] = field( default=None, @@ -99,7 +104,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) d: Optional[RootD] = field( default=None, @@ -107,7 +112,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) @dataclass @@ -122,11 +127,12 @@ class A: dolor sit amet, consectetur adipiscing elit. Vivamus efficitur. My\\Ipsum """ + sub_a: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", "required": True, - } + }, ) diff --git a/tests/fixtures/docstrings/rst/schema.py b/tests/fixtures/docstrings/rst/schema.py index d8eafd39a..6eb06e46b 100644 --- a/tests/fixtures/docstrings/rst/schema.py +++ b/tests/fixtures/docstrings/rst/schema.py @@ -11,6 +11,7 @@ class DoubleQuotesDescription: Dont trip on quotes: "A", "B", "C", "D" My\\Ipsum """ + class Meta: namespace = "urn:docs" @@ -18,6 +19,7 @@ class Meta: @dataclass class DoubleQuotesSummary: """Dont trip on quotes: "A", "B", "C", "D" My\\Ipsum""" + class Meta: namespace = "urn:docs" @@ -28,8 +30,9 @@ class RootEnum(Enum): :cvar B: Lorem ipsum dolor '''sit''' amet, consectetur adipiscing elit. Morbi dapibus. My\\Ipsum """ - A = 'A' - B = 'B' + + A = "A" + B = "B" class RootB(Enum): @@ -38,13 +41,14 @@ class RootB(Enum): dolor sit amet, consectetur adipiscing elit. Etiam mollis. :cvar NO: Lorem ipsum dolor My\\Ipsum """ - YES = 'Yes' - NO = 'No' + + YES = "Yes" + NO = "No" class RootD(Enum): - TRUE = 'true' - FALSE = 'false' + TRUE = "true" + FALSE = "false" @dataclass @@ -63,6 +67,7 @@ class Root: :ivar c: :ivar d: """ + class Meta: namespace = "urn:docs" @@ -72,7 +77,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) b: Optional[RootB] = field( default=None, @@ -80,7 +85,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) c: Optional[RootEnum] = field( default=None, @@ -88,7 +93,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) d: Optional[RootD] = field( default=None, @@ -96,7 +101,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) @dataclass @@ -108,11 +113,12 @@ class A: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus efficitur. My\\Ipsum """ + sub_a: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", "required": True, - } + }, ) diff --git a/tests/fixtures/dtd/models/complete_example.py b/tests/fixtures/dtd/models/complete_example.py index 913126717..47fadb869 100644 --- a/tests/fixtures/dtd/models/complete_example.py +++ b/tests/fixtures/dtd/models/complete_example.py @@ -4,8 +4,8 @@ class PostStatus(Enum): - DRAFT = 'draft' - PUBLISHED = 'published' + DRAFT = "draft" + PUBLISHED = "published" @dataclass @@ -15,7 +15,7 @@ class Tags: metadata={ "name": "Tag", "type": "Element", - } + }, ) @@ -26,43 +26,43 @@ class Post: metadata={ "type": "Attribute", "required": True, - } + }, ) lang: str = field( - default='en', + default="en", metadata={ "type": "Attribute", "namespace": "http://www.w3.org/XML/1998/namespace", "required": True, - } + }, ) created_at: Optional[str] = field( default=None, metadata={ "type": "Attribute", "required": True, - } + }, ) author: Optional[str] = field( default=None, metadata={ "type": "Attribute", "required": True, - } + }, ) origin: List[str] = field( default_factory=list, metadata={ "name": "Origin", "type": "Element", - } + }, ) source: List[str] = field( default_factory=list, metadata={ "name": "Source", "type": "Element", - } + }, ) title: Optional[str] = field( default=None, @@ -70,7 +70,7 @@ class Post: "name": "Title", "type": "Element", "required": True, - } + }, ) body: Optional[str] = field( default=None, @@ -78,7 +78,7 @@ class Post: "name": "Body", "type": "Element", "required": True, - } + }, ) tags: Optional[Tags] = field( default=None, @@ -86,7 +86,7 @@ class Post: "name": "Tags", "type": "Element", "required": True, - } + }, ) @@ -98,5 +98,5 @@ class Blog: "name": "Post", "type": "Element", "min_occurs": 1, - } + }, ) diff --git a/tests/fixtures/hello/hello.py b/tests/fixtures/hello/hello.py index f733ed9e2..498bc4cd9 100644 --- a/tests/fixtures/hello/hello.py +++ b/tests/fixtures/hello/hello.py @@ -14,7 +14,7 @@ class Meta: metadata={ "type": "Element", "namespace": "", - } + }, ) @@ -28,7 +28,7 @@ class Meta: metadata={ "type": "Element", "namespace": "", - } + }, ) @@ -43,7 +43,7 @@ class Meta: metadata={ "type": "Element", "namespace": "", - } + }, ) @@ -59,7 +59,7 @@ class Meta: "name": "return", "type": "Element", "namespace": "", - } + }, ) @@ -74,7 +74,7 @@ class Meta: metadata={ "name": "Body", "type": "Element", - } + }, ) @dataclass @@ -85,7 +85,7 @@ class Body: "name": "getHelloAsString", "type": "Element", "namespace": "http://hello/", - } + }, ) @@ -100,25 +100,27 @@ class Meta: metadata={ "name": "Body", "type": "Element", - } + }, ) @dataclass class Body: - get_hello_as_string_response: Optional[GetHelloAsStringResponse] = field( + get_hello_as_string_response: Optional[ + GetHelloAsStringResponse + ] = field( default=None, metadata={ "name": "getHelloAsStringResponse", "type": "Element", "namespace": "http://hello/", - } + }, ) fault: Optional["HelloGetHelloAsStringOutput.Body.Fault"] = field( default=None, metadata={ "name": "Fault", "type": "Element", - } + }, ) @dataclass @@ -128,28 +130,30 @@ class Fault: metadata={ "type": "Element", "namespace": "", - } + }, ) faultstring: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", - } + }, ) faultactor: Optional[str] = field( default=None, metadata={ "type": "Element", "namespace": "", - } + }, ) - detail: Optional["HelloGetHelloAsStringOutput.Body.Fault.Detail"] = field( + detail: Optional[ + "HelloGetHelloAsStringOutput.Body.Fault.Detail" + ] = field( default=None, metadata={ "type": "Element", "namespace": "", - } + }, ) @dataclass @@ -160,7 +164,7 @@ class Detail: "name": "HelloError", "type": "Element", "namespace": "http://hello/", - } + }, ) hello_bye_error: Optional[HelloByeError] = field( default=None, @@ -168,7 +172,7 @@ class Detail: "name": "HelloByeError", "type": "Element", "namespace": "http://hello/", - } + }, ) diff --git a/tests/fixtures/primer/order.py b/tests/fixtures/primer/order.py index db649f92d..1658a28ba 100644 --- a/tests/fixtures/primer/order.py +++ b/tests/fixtures/primer/order.py @@ -11,7 +11,7 @@ class Items: metadata={ "type": "Element", "namespace": "", - } + }, ) @dataclass @@ -28,6 +28,7 @@ class Item: part_num Stock Keeping Unit """ + product_name: Optional[str] = field( default=None, metadata={ @@ -35,7 +36,7 @@ class Item: "type": "Element", "namespace": "", "required": True, - } + }, ) quantity: Optional[int] = field( default=None, @@ -44,7 +45,7 @@ class Item: "namespace": "", "required": True, "max_exclusive": 100, - } + }, ) usprice: Optional[Decimal] = field( default=None, @@ -53,13 +54,13 @@ class Item: "type": "Element", "namespace": "", "required": True, - } + }, ) comment: Optional[str] = field( default=None, metadata={ "type": "Element", - } + }, ) ship_date: Optional[XmlDate] = field( default=None, @@ -67,7 +68,7 @@ class Item: "name": "shipDate", "type": "Element", "namespace": "", - } + }, ) part_num: Optional[str] = field( default=None, @@ -76,7 +77,7 @@ class Item: "type": "Attribute", "required": True, "pattern": r"\d{3}-[A-Z]{2}", - } + }, ) @@ -91,7 +92,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) street: Optional[str] = field( default=None, @@ -99,7 +100,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) city: Optional[str] = field( default=None, @@ -107,7 +108,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) state: Optional[str] = field( default=None, @@ -115,7 +116,7 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) zip: Optional[Decimal] = field( default=None, @@ -123,14 +124,14 @@ class Meta: "type": "Element", "namespace": "", "required": True, - } + }, ) country: str = field( init=False, - default='US', + default="US", metadata={ "type": "Attribute", - } + }, ) @@ -140,10 +141,10 @@ class Meta: name = "comment" value: str = field( - default='', + default="", metadata={ "required": True, - } + }, ) @@ -163,6 +164,7 @@ class PurchaseOrderType: items order_date """ + ship_to: Optional[Usaddress] = field( default=None, metadata={ @@ -170,7 +172,7 @@ class PurchaseOrderType: "type": "Element", "namespace": "", "required": True, - } + }, ) bill_to: Optional[Usaddress] = field( default=None, @@ -179,13 +181,13 @@ class PurchaseOrderType: "type": "Element", "namespace": "", "required": True, - } + }, ) comment: Optional[str] = field( default=None, metadata={ "type": "Element", - } + }, ) items: Optional[Items] = field( default=None, @@ -193,14 +195,14 @@ class PurchaseOrderType: "type": "Element", "namespace": "", "required": True, - } + }, ) order_date: Optional[XmlDate] = field( default=None, metadata={ "name": "orderDate", "type": "Attribute", - } + }, ) diff --git a/tests/fixtures/series/series.py b/tests/fixtures/series/series.py index ad4088a79..53d876e57 100644 --- a/tests/fixtures/series/series.py +++ b/tests/fixtures/series/series.py @@ -13,20 +13,20 @@ class Meta: metadata={ "type": "Element", "required": True, - } + }, ) code: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) timezone: Optional[str] = field( default=None, metadata={ "type": "Element", - } + }, ) @@ -40,20 +40,20 @@ class Meta: metadata={ "type": "Element", "required": True, - } + }, ) thetvdb: Optional[int] = field( default=None, metadata={ "type": "Element", - } + }, ) imdb: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) @@ -67,14 +67,14 @@ class Meta: metadata={ "type": "Element", "required": True, - } + }, ) original: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) @@ -88,7 +88,7 @@ class Meta: metadata={ "type": "Element", "required": True, - } + }, ) @@ -102,7 +102,7 @@ class Meta: metadata={ "type": "Element", "required": True, - } + }, ) @@ -116,14 +116,14 @@ class Meta: metadata={ "type": "Element", "required": True, - } + }, ) days: List[str] = field( default_factory=list, metadata={ "type": "Element", "min_occurs": 1, - } + }, ) @@ -137,7 +137,7 @@ class Meta: metadata={ "type": "Element", "required": True, - } + }, ) @@ -152,14 +152,14 @@ class Meta: "name": "self", "type": "Element", "required": True, - } + }, ) previousepisode: Optional[Previousepisode] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) @@ -172,21 +172,21 @@ class Meta: default=None, metadata={ "type": "Element", - } + }, ) name: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) country: Optional[Country] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) @@ -200,20 +200,20 @@ class Meta: metadata={ "type": "Element", "required": True, - } + }, ) url: Optional[str] = field( default=None, metadata={ "type": "Element", - } + }, ) name: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) type_value: Optional[str] = field( default=None, @@ -221,42 +221,42 @@ class Meta: "name": "type", "type": "Element", "required": True, - } + }, ) language: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) genres: List[str] = field( default_factory=list, metadata={ "type": "Element", "min_occurs": 1, - } + }, ) status: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) runtime: Optional[int] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) premiered: Optional[XmlDate] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) official_site: Optional[str] = field( default=None, @@ -264,73 +264,73 @@ class Meta: "name": "officialSite", "type": "Element", "required": True, - } + }, ) schedule: Optional[Schedule] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) rating: Optional[Rating] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) weight: Optional[int] = field( default=None, metadata={ "type": "Element", - } + }, ) network: Optional[Network] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) web_channel: Optional[object] = field( default=None, metadata={ "name": "webChannel", "type": "Element", - } + }, ) externals: Optional[Externals] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) image: Optional[Image] = field( default=None, metadata={ "type": "Element", - } + }, ) summary: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) updated: Optional[int] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) links: Optional[Links] = field( default=None, metadata={ "name": "_links", "type": "Element", - } + }, ) diff --git a/tests/fixtures/stripe/models/balance.py b/tests/fixtures/stripe/models/balance.py index 3a2dbf606..47719fb8f 100644 --- a/tests/fixtures/stripe/models/balance.py +++ b/tests/fixtures/stripe/models/balance.py @@ -12,14 +12,14 @@ class Meta: metadata={ "type": "Element", "required": True, - } + }, ) currency: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) @@ -33,14 +33,14 @@ class Meta: metadata={ "type": "Element", "required": True, - } + }, ) card: Optional[int] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) @@ -54,21 +54,21 @@ class Meta: metadata={ "type": "Element", "required": True, - } + }, ) currency: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) source_types: Optional[SourceTypes] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) @@ -82,21 +82,21 @@ class Meta: metadata={ "type": "Element", "required": True, - } + }, ) currency: Optional[str] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) source_types: Optional[SourceTypes] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) @@ -111,33 +111,33 @@ class Meta: "name": "object", "type": "Element", "required": True, - } + }, ) available: Tuple[Available, ...] = field( default_factory=tuple, metadata={ "type": "Element", "min_occurs": 1, - } + }, ) connect_reserved: Tuple[ConnectReserved, ...] = field( default_factory=tuple, metadata={ "type": "Element", "min_occurs": 1, - } + }, ) livemode: Optional[bool] = field( default=None, metadata={ "type": "Element", "required": True, - } + }, ) pending: Tuple[Pending, ...] = field( default_factory=tuple, metadata={ "type": "Element", "min_occurs": 1, - } + }, ) diff --git a/xsdata/codegen/writer.py b/xsdata/codegen/writer.py index d1e529c54..0301631f4 100644 --- a/xsdata/codegen/writer.py +++ b/xsdata/codegen/writer.py @@ -1,3 +1,6 @@ +import subprocess +from pathlib import Path +from textwrap import indent from typing import ClassVar, Dict, List, Type from xsdata.codegen.models import Class @@ -34,7 +37,7 @@ def write(self, classes: List[Class]): for result in self.generator.render(classes): if result.source.strip(): logger.info("Generating package: %s", result.title) - src_code = header + result.source + src_code = self.ruff_code(header + result.source, result.path) result.path.parent.mkdir(parents=True, exist_ok=True) result.path.write_text(src_code, encoding="utf-8") @@ -42,9 +45,11 @@ def print(self, classes: List[Class]): """Iterate over the designated generator outputs and print them to the console.""" self.generator.normalize_packages(classes) + header = self.generator.render_header() for result in self.generator.render(classes): if result.source.strip(): - print(result.source, end="") + src_code = self.ruff_code(header + result.source, result.path) + print(src_code, end="") @classmethod def from_config(cls, config: GeneratorConfig) -> "CodeWriter": @@ -63,3 +68,31 @@ def register_generator(cls, name: str, clazz: Type[AbstractGenerator]): @classmethod def unregister_generator(cls, name: str): cls.generators.pop(name) + + def ruff_code(self, src_code: str, file_path: Path) -> str: + """Run ruff format on the src code.""" + commands = [ + [ + "ruff", + "format", + "--stdin-filename", + str(file_path), + "--line-length", + str(self.generator.config.output.max_line_length), + ], + ] + try: + src_code_encoded = src_code.encode() + for command in commands: + result = subprocess.run( + command, + input=src_code_encoded, + capture_output=True, + check=True, + ) + src_code_encoded = result.stdout + + return src_code_encoded.decode() + except subprocess.CalledProcessError as e: + error = indent(e.stderr.decode(), " ") + raise CodeGenerationError(f"Ruff failed:\n{error}")