From f7d7910149b0dcd93ca8af94484404ad930f8567 Mon Sep 17 00:00:00 2001 From: dschwartznyc Date: Mon, 30 Mar 2026 15:56:49 -0400 Subject: [PATCH] feat: add project name override for pyproject.toml generation --- .../generator/python/PythonCodeGenerator.java | 18 ++++++++++- .../python/PythonCodeGeneratorCLI.java | 30 +++++++++++++++---- .../python/util/PythonCodeGeneratorUtil.java | 11 +++++-- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGenerator.java b/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGenerator.java index f54af1ca..97eb92f9 100644 --- a/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGenerator.java +++ b/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGenerator.java @@ -97,11 +97,27 @@ public class PythonCodeGenerator extends AbstractExternalGenerator { private Map contexts = null; + /** + * Optional override for the pyproject.toml project name. + * When null, the name is derived from the namespace as "python-<first-segment>". + */ + private String projectName = null; + public PythonCodeGenerator() { super(PYTHON); contexts = new HashMap<>(); } + /** + * Overrides the pyproject.toml project name. When not set (or set to null), + * the name is derived from the namespace as "python-<first-segment>". + * + * @param projectName the project name, or null for default behaviour + */ + public void setProjectName(String projectName) { + this.projectName = projectName; + } + @Override public Map beforeAllGenerate(ResourceSet set, Collection models, String version) { @@ -180,7 +196,7 @@ private Map processDAG(String nameSpace, PythonCodeGenerat Set enumImports = context.getEnumImports(); if (nameSpaceObjects != null && !nameSpaceObjects.isEmpty() && dependencyDAG != null && enumImports != null) { - result.put(PYPROJECT_TOML, PythonCodeGeneratorUtil.createPYProjectTomlFile(nameSpace, cleanVersion)); + result.put(PYPROJECT_TOML, PythonCodeGeneratorUtil.createPYProjectTomlFile(nameSpace, cleanVersion, projectName)); PythonCodeWriter bundleWriter = new PythonCodeWriter(); TopologicalOrderIterator topologicalOrderIterator = new TopologicalOrderIterator<>( dependencyDAG); diff --git a/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLI.java b/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLI.java index d98f4b21..689ba54a 100644 --- a/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLI.java +++ b/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLI.java @@ -96,6 +96,9 @@ public int execute(String[] args) { .desc("Continue generation even if validation errors occur").build(); Option failOnWarningsOpt = Option.builder("w").longOpt("fail-on-warnings") .desc("Treat validation warnings as errors").build(); + Option projectNameOpt = Option.builder("n").longOpt("project-name").argName("projectName") + .desc("Override the pyproject.toml project name (default: python-)") + .hasArg().build(); options.addOption(help); options.addOption(srcDirOpt); @@ -103,6 +106,7 @@ public int execute(String[] args) { options.addOption(tgtDirOpt); options.addOption(allowErrorsOpt); options.addOption(failOnWarningsOpt); + options.addOption(projectNameOpt); CommandLineParser parser = new DefaultParser(); try { @@ -114,13 +118,14 @@ public int execute(String[] args) { String tgtDir = cmd.getOptionValue("t", "./python"); boolean allowErrors = cmd.hasOption("e"); boolean failOnWarnings = cmd.hasOption("w"); + String projectName = cmd.getOptionValue("n"); if (cmd.hasOption("s")) { String srcDir = cmd.getOptionValue("s"); - return translateFromSourceDir(srcDir, tgtDir, allowErrors, failOnWarnings); + return translateFromSourceDir(srcDir, tgtDir, allowErrors, failOnWarnings, projectName); } else if (cmd.hasOption("f")) { String srcFile = cmd.getOptionValue("f"); - return translateFromSourceFile(srcFile, tgtDir, allowErrors, failOnWarnings); + return translateFromSourceFile(srcFile, tgtDir, allowErrors, failOnWarnings, projectName); } else { System.err.println("Either a source directory (-s) or source file (-f) must be specified."); printUsage(options); @@ -139,7 +144,11 @@ private static void printUsage(Options options) { } protected int translateFromSourceDir(String srcDir, String tgtDir, boolean allowErrors, boolean failOnWarnings) { - // Find all .rosetta files in a directory + return translateFromSourceDir(srcDir, tgtDir, allowErrors, failOnWarnings, null); + } + + protected int translateFromSourceDir(String srcDir, String tgtDir, boolean allowErrors, boolean failOnWarnings, + String projectName) { Path srcDirPath = Paths.get(srcDir); if (!Files.exists(srcDirPath)) { LOGGER.error("Source directory does not exist: {}", srcDir); @@ -154,7 +163,7 @@ protected int translateFromSourceDir(String srcDir, String tgtDir, boolean allow .filter(Files::isRegularFile) .filter(f -> f.getFileName().toString().endsWith(".rosetta")) .collect(Collectors.toList()); - return processRosettaFiles(rosettaFiles, tgtDir, allowErrors, failOnWarnings); + return processRosettaFiles(rosettaFiles, tgtDir, allowErrors, failOnWarnings, projectName); } catch (IOException e) { LOGGER.error("Failed to process source directory: {}", srcDir, e); return 1; @@ -162,6 +171,11 @@ protected int translateFromSourceDir(String srcDir, String tgtDir, boolean allow } protected int translateFromSourceFile(String srcFile, String tgtDir, boolean allowErrors, boolean failOnWarnings) { + return translateFromSourceFile(srcFile, tgtDir, allowErrors, failOnWarnings, null); + } + + protected int translateFromSourceFile(String srcFile, String tgtDir, boolean allowErrors, boolean failOnWarnings, + String projectName) { Path srcFilePath = Paths.get(srcFile); if (!Files.exists(srcFilePath)) { LOGGER.error("Source file does not exist: {}", srcFile); @@ -176,7 +190,7 @@ protected int translateFromSourceFile(String srcFile, String tgtDir, boolean all return 1; } List rosettaFiles = List.of(srcFilePath); - return processRosettaFiles(rosettaFiles, tgtDir, allowErrors, failOnWarnings); + return processRosettaFiles(rosettaFiles, tgtDir, allowErrors, failOnWarnings, projectName); } protected IResourceValidator getValidator(Injector injector) { @@ -186,6 +200,11 @@ protected IResourceValidator getValidator(Injector injector) { // Common processing function protected int processRosettaFiles(List rosettaFiles, String tgtDir, boolean allowErrors, boolean failOnWarnings) { + return processRosettaFiles(rosettaFiles, tgtDir, allowErrors, failOnWarnings, null); + } + + protected int processRosettaFiles(List rosettaFiles, String tgtDir, boolean allowErrors, + boolean failOnWarnings, String projectName) { LOGGER.info("Processing {} .rosetta files, writing to: {}", rosettaFiles.size(), tgtDir); if (rosettaFiles.isEmpty()) { @@ -204,6 +223,7 @@ protected int processRosettaFiles(List rosettaFiles, String tgtDir, boolea .forEach(resources::add); PythonCodeGenerator pythonCodeGenerator = injector.getInstance(PythonCodeGenerator.class); + pythonCodeGenerator.setProjectName(projectName); PythonModelLoader modelLoader = injector.getInstance(PythonModelLoader.class); List models = modelLoader.getRosettaModels(resources); diff --git a/src/main/java/com/regnosys/rosetta/generator/python/util/PythonCodeGeneratorUtil.java b/src/main/java/com/regnosys/rosetta/generator/python/util/PythonCodeGeneratorUtil.java index 0e966f43..3abb4349 100644 --- a/src/main/java/com/regnosys/rosetta/generator/python/util/PythonCodeGeneratorUtil.java +++ b/src/main/java/com/regnosys/rosetta/generator/python/util/PythonCodeGeneratorUtil.java @@ -95,13 +95,20 @@ public static String getNamespace(RosettaModel rm) { } public static String createPYProjectTomlFile(String namespace, String version) { + return createPYProjectTomlFile(namespace, version, null); + } + + public static String createPYProjectTomlFile(String namespace, String version, String projectName) { + String name = (projectName != null && !projectName.isBlank()) + ? projectName + : "python-" + namespace.split("\\.")[0]; return """ [build-system] requires = ["setuptools>=62.0"] build-backend = "setuptools.build_meta" [project] - name = "python-%s" + name = "%s" version = "%s" requires-python = ">= 3.11" dependencies = [ @@ -109,7 +116,7 @@ public static String createPYProjectTomlFile(String namespace, String version) { "rune.runtime>=1.0.0,<2.0.0" ] [tool.setuptools.packages.find] - where = ["src"]""".formatted(namespace, version).stripIndent(); + where = ["src"]""".formatted(name, version).stripIndent(); } public static String cleanVersion(String version) {