diff --git a/docs/generators/python.md b/docs/generators/python.md
index 791bc88a5a50..bc3a0feb7b83 100644
--- a/docs/generators/python.md
+++ b/docs/generators/python.md
@@ -19,6 +19,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
+|buildSystem|Build system to use in pyproject.toml (setuptools, hatchling).| |setuptools|
|dateFormat|date format for query parameters| |%Y-%m-%d|
|datetimeFormat|datetime format for query parameters| |%Y-%m-%dT%H:%M:%S%z|
|disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|
- **false**
- The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.
- **true**
- Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.
|true|
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java
index e26d5ff71c3a..0a02a1265526 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java
@@ -49,6 +49,7 @@ public class PythonClientCodegen extends AbstractPythonCodegen implements Codege
public static final String SET_ENSURE_ASCII_TO_FALSE = "setEnsureAsciiToFalse";
public static final String POETRY1_FALLBACK = "poetry1";
public static final String LAZY_IMPORTS = "lazyImports";
+ public static final String BUILD_SYSTEM = "buildSystem";
@Setter protected String packageUrl;
protected String apiDocPath = "docs/";
@@ -153,6 +154,7 @@ public PythonClientCodegen() {
cliOptions.add(new CliOption(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP, CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP_DESC).defaultValue("false"));
cliOptions.add(new CliOption(POETRY1_FALLBACK, "Fallback to formatting pyproject.toml to Poetry 1.x format."));
cliOptions.add(new CliOption(LAZY_IMPORTS, "Enable lazy imports.").defaultValue(Boolean.FALSE.toString()));
+ cliOptions.add(new CliOption(BUILD_SYSTEM, "Build system to use in pyproject.toml (setuptools, hatchling).").defaultValue("setuptools"));
supportedLibraries.put("urllib3", "urllib3-based client");
supportedLibraries.put("asyncio", "asyncio-based client");
@@ -271,6 +273,13 @@ public void processOpts() {
additionalProperties.put(LAZY_IMPORTS, Boolean.valueOf(additionalProperties.get(LAZY_IMPORTS).toString()));
}
+ if (additionalProperties.containsKey(BUILD_SYSTEM)) {
+ String buildSystem = (String) additionalProperties.get(BUILD_SYSTEM);
+ if ("hatchling".equals(buildSystem)) {
+ additionalProperties.put("hatchling", true);
+ }
+ }
+
String modelPath = packagePath() + File.separatorChar + modelPackage.replace('.', File.separatorChar);
String apiPath = packagePath() + File.separatorChar + apiPackage.replace('.', File.separatorChar);
diff --git a/modules/openapi-generator/src/main/resources/python/pyproject.mustache b/modules/openapi-generator/src/main/resources/python/pyproject.mustache
index c6d5328337d7..315d750bacd8 100644
--- a/modules/openapi-generator/src/main/resources/python/pyproject.mustache
+++ b/modules/openapi-generator/src/main/resources/python/pyproject.mustache
@@ -110,8 +110,14 @@ mypy = ">= 1.5"
[build-system]
+{{#hatchling}}
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+{{/hatchling}}
+{{^hatchling}}
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
+{{/hatchling}}
[tool.pylint.'MESSAGES CONTROL']
extension-pkg-whitelist = "pydantic"
diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java
index 0ec48cfb7072..f5bc24393993 100644
--- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java
+++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java
@@ -670,6 +670,47 @@ public void testUuidWithPatternImportsFieldValidator() throws IOException {
assertFileContains(p, "from pydantic import BaseModel, ConfigDict, field_validator");
}
+ @Test(description = "Verify default buildSystem uses setuptools")
+ public void testDefaultBuildSystemSetuptools() throws IOException {
+ File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
+ output.deleteOnExit();
+
+ final CodegenConfigurator configurator = new CodegenConfigurator()
+ .setGeneratorName("python")
+ .setInputSpec("src/test/resources/bugs/issue_21619.yaml")
+ .setOutputDir(output.getAbsolutePath());
+
+ DefaultGenerator generator = new DefaultGenerator();
+ List files = generator.opts(configurator.toClientOptInput()).generate();
+ files.forEach(File::deleteOnExit);
+
+ Path pyprojectPath = Paths.get(output.getAbsolutePath(), "pyproject.toml");
+ TestUtils.assertFileExists(pyprojectPath);
+ TestUtils.assertFileContains(pyprojectPath, "requires = [\"setuptools\"]");
+ TestUtils.assertFileContains(pyprojectPath, "build-backend = \"setuptools.build_meta\"");
+ }
+
+ @Test(description = "Verify buildSystem=hatchling uses hatchling")
+ public void testBuildSystemHatchling() throws IOException {
+ File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
+ output.deleteOnExit();
+
+ final CodegenConfigurator configurator = new CodegenConfigurator()
+ .setGeneratorName("python")
+ .setInputSpec("src/test/resources/bugs/issue_21619.yaml")
+ .setOutputDir(output.getAbsolutePath())
+ .addAdditionalProperty("buildSystem", "hatchling");
+
+ DefaultGenerator generator = new DefaultGenerator();
+ List files = generator.opts(configurator.toClientOptInput()).generate();
+ files.forEach(File::deleteOnExit);
+
+ Path pyprojectPath = Paths.get(output.getAbsolutePath(), "pyproject.toml");
+ TestUtils.assertFileExists(pyprojectPath);
+ TestUtils.assertFileContains(pyprojectPath, "requires = [\"hatchling\"]");
+ TestUtils.assertFileContains(pyprojectPath, "build-backend = \"hatchling.build\"");
+ }
+
@Test(description = "Verify non-poetry1 mode uses object notation for license")
public void testNonPoetry1LicenseFormat() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();