Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ The recommended way to use conjure-python is via a build tool like [gradle-conju

Usage: conjure-python generate <input> <output> [...options]

--packageName package name that will appear in setup.py
--packageVersion version number that will appear in setup.py
--packageDescription description that will appear in setup.py
--packageUrl url that will appear in setup.py
--packageAuthor author that will appear in setup.py
--writeCondaRecipe use this boolean option to generate a `conda_recipe/meta.yaml`
--packageName package name that will appear in setup.py or pyproject.toml
--packageVersion version number that will appear in setup.py or pyproject.toml
--packageDescription description that will appear in setup.py or pyproject.toml
--packageUrl url that will appear in setup.py or pyproject.toml
--packageAuthor author that will appear in setup.py or pyproject.toml
--writeCondaRecipe use this boolean option to generate a `conda_recipe/meta.yaml`
--writePyprojectToml use this boolean option to generate a `pyproject.toml` with Hatch instead of `setup.py` (default: false)

## Example generated objects

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.palantir.conjure.python.poet.PythonLine;
import com.palantir.conjure.python.poet.PythonMetaYaml;
import com.palantir.conjure.python.poet.PythonPackage;
import com.palantir.conjure.python.poet.PythonPyprojectToml;
import com.palantir.conjure.python.poet.PythonSetup;
import com.palantir.conjure.python.poet.PythonSnippet;
import com.palantir.conjure.python.processors.packagename.CompoundPackageNameProcessor;
Expand Down Expand Up @@ -78,7 +79,11 @@ public void write(ConjureDefinition conjureDefinition, PythonFileWriter writer)

PythonPackage rootPackage = PythonPackage.of(buildPackageNameProcessor().process(""));
if (!config.generateRawSource()) {
writer.writePythonFile(buildPythonSetupFile(rootPackage));
if (config.shouldWritePyprojectToml()) {
writer.writePythonFile(buildPyprojectTomlFile(rootPackage));
} else {
writer.writePythonFile(buildPythonSetupFile(rootPackage));
}
writer.writePythonFile(buildPyTypedFile());
}
if (config.shouldWriteCondaRecipe()) {
Expand Down Expand Up @@ -274,6 +279,26 @@ private PythonFile buildPythonSetupFile(PythonPackage rootPackage) {
.build();
}

private PythonFile buildPyprojectTomlFile(PythonPackage rootPackage) {
PythonPyprojectToml.Builder builder = PythonPyprojectToml.builder()
.pythonPackage(rootPackage)
.putOptions("name", config.packageName().get())
.putOptions("version", config.packageVersion().get())
.putOptions("requires-python", ">=3.8")
.addInstallDependencies("requests")
.addInstallDependencies(String.format(
"conjure-python-client>=%s,<%s",
config.minConjureClientVersion(), config.maxConjureClientVersion()));
config.packageDescription().ifPresent(value -> builder.putOptions("description", value));
config.packageUrl().ifPresent(url -> builder.putUrlOptions("Homepage", url));

return PythonFile.builder()
.pythonPackage(PythonPackage.of("."))
.fileName("pyproject.toml")
.addContents(builder.build())
.build();
}

/**
* Mark a package as containing type annotations.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

package com.palantir.conjure.python;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.google.common.base.Splitter;
import com.palantir.tokens.auth.ImmutablesStyle;
import java.util.Optional;
import org.immutables.value.Value;

@Value.Immutable
@ImmutablesStyle
@JsonDeserialize(as = ImmutableGeneratorConfiguration.class)
public interface GeneratorConfiguration {

Optional<String> packageName();
Expand All @@ -41,6 +43,8 @@ public interface GeneratorConfiguration {

boolean shouldWriteCondaRecipe();

boolean shouldWritePyprojectToml();

boolean generateRawSource();

default Optional<String> pythonicPackageName() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* (c) Copyright 2018 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.palantir.conjure.python.poet;

import com.google.common.collect.ImmutableSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.immutables.value.Value;

@Value.Immutable
public interface PythonPyprojectToml extends PythonSnippet {

@Override
@Value.Default
default String idForSorting() {
return "pyproject";
}

@Override
@Value.Default
default Set<PythonImport> imports() {
return ImmutableSet.of();
}

Map<String, String> options();

Map<String, String> urlOptions();

List<String> installDependencies();

@Override
default void emit(PythonPoetWriter poetWriter) {
poetWriter.maintainingIndent(() -> {
// [build-system]
poetWriter.writeIndentedLine("[build-system]");
poetWriter.writeIndentedLine("requires = [\"hatchling\"]");
poetWriter.writeIndentedLine("build-backend = \"hatchling.build\"");
poetWriter.writeIndentedLine("");

// [project]
poetWriter.writeIndentedLine("[project]");

// Write string options
options().forEach((key, value) -> {
poetWriter.writeIndentedLine("%s = \"%s\"", key, escapeTomlString(value));
});

// Write dependencies
if (!installDependencies().isEmpty()) {
poetWriter.writeIndentedLine("dependencies = [");
poetWriter.increaseIndent();
installDependencies().forEach(dependency -> {
poetWriter.writeIndentedLine("\"%s\",", escapeTomlString(dependency));
});
poetWriter.decreaseIndent();
poetWriter.writeIndentedLine("]");
}

// Write [project.urls] section if present
if (!urlOptions().isEmpty()) {
poetWriter.writeIndentedLine("");
poetWriter.writeIndentedLine("[project.urls]");
urlOptions().forEach((key, value) -> {
poetWriter.writeIndentedLine("%s = \"%s\"", key, escapeTomlString(value));
});
}

// [tool.hatch.build.targets.wheel]
poetWriter.writeIndentedLine("");
poetWriter.writeIndentedLine("[tool.hatch.build.targets.wheel]");
poetWriter.writeIndentedLine("packages = [\".\"]");
});
}

/**
* Escapes special characters in TOML strings.
*/
static String escapeTomlString(String value) {
return value.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t");
}

class Builder extends ImmutablePythonPyprojectToml.Builder {}

static Builder builder() {
return new Builder();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

import static org.assertj.core.api.Assertions.assertThat;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.palantir.conjure.defs.Conjure;
import com.palantir.conjure.spec.ConjureDefinition;
import java.io.File;
Expand All @@ -35,23 +37,19 @@
@RunWith(ConjureSubfolderRunner.class)
public final class ConjurePythonGeneratorTest {

private final ConjurePythonGenerator generator = new ConjurePythonGenerator(GeneratorConfiguration.builder()
.packageName("package-name")
.packageVersion("0.0.0")
.packageDescription("project description")
.minConjureClientVersion("2.8.0")
.maxConjureClientVersion("4")
.generatorVersion("0.0.0")
.shouldWriteCondaRecipe(true)
.generateRawSource(false)
.build());
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().registerModule(new Jdk8Module());

private final InMemoryPythonFileWriter pythonFileWriter = new InMemoryPythonFileWriter();

@ConjureSubfolderRunner.Test
public void assertThatFilesRenderAsExpected(Path folder) throws IOException {
Path configPath = folder.resolve("generator-config.json");
GeneratorConfiguration config = OBJECT_MAPPER.readValue(configPath.toFile(), GeneratorConfiguration.class);
ConjurePythonGenerator generator = new ConjurePythonGenerator(config);

Path expected = folder.resolve("expected");
ConjureDefinition definition = getInputDefinitions(folder);
maybeResetExpectedDirectory(expected, definition);
maybeResetExpectedDirectory(expected, definition, generator);

generator.write(definition, pythonFileWriter);
assertFoldersEqual(expected);
Expand All @@ -76,7 +74,8 @@ private void assertFoldersEqual(Path expected) throws IOException {
System.out.println(count + " files checked");
}

private void maybeResetExpectedDirectory(Path expected, ConjureDefinition definition) throws IOException {
private void maybeResetExpectedDirectory(
Path expected, ConjureDefinition definition, ConjurePythonGenerator generator) throws IOException {
if (Boolean.parseBoolean(System.getProperty("recreate", "false"))
|| !expected.toFile().isDirectory()) {
Files.createDirectories(expected);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# coding=utf-8
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "package-name"
version = "0.0.0"
requires-python = ">=3.8"
description = "project description"
dependencies = [
"requests",
"conjure-python-client>=2.8.0,<4",
]

[tool.hatch.build.targets.wheel]
packages = ["."]
18 changes: 0 additions & 18 deletions conjure-python-core/src/test/resources/services/expected/setup.py

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"packageName": "package-name",
"packageVersion": "0.0.0",
"packageDescription": "project description",
"minConjureClientVersion": "2.8.0",
"maxConjureClientVersion": "4",
"generatorVersion": "0.0.0",
"shouldWriteCondaRecipe": false,
"shouldWritePyprojectToml": true,
"generateRawSource": false
}
11 changes: 11 additions & 0 deletions conjure-python-core/src/test/resources/types/generator-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"packageName": "package-name",
"packageVersion": "0.0.0",
"packageDescription": "project description",
"minConjureClientVersion": "2.8.0",
"maxConjureClientVersion": "4",
"generatorVersion": "0.0.0",
"shouldWriteCondaRecipe": true,
"shouldWritePyprojectToml": false,
"generateRawSource": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ boolean shouldWriteCondaRecipe() {
return false;
}

@Value.Default
@SuppressWarnings("DesignForExtension")
boolean shouldWritePyprojectToml() {
return false;
}

@Value.Check
final void check() {
Preconditions.checkArgument(input().isFile(), "Target must exist and be a file");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ public static final class GenerateCommand implements Runnable {
description = "Generate a `conda_recipe/meta.yaml`")
private boolean writeCondaRecipe;

@CommandLine.Option(
names = "--writePyprojectToml",
defaultValue = "false",
description = "Generate a `pyproject.toml` file using Hatch instead of `setup.py`")
private boolean writePyprojectToml;

@CommandLine.Unmatched
@SuppressWarnings("StrictUnusedVariable")
private List<String> unmatchedOptions;
Expand Down Expand Up @@ -118,6 +124,7 @@ CliConfiguration getConfiguration() {
.packageUrl(Optional.ofNullable(packageUrl))
.generateRawSource(rawSource)
.shouldWriteCondaRecipe(writeCondaRecipe)
.shouldWritePyprojectToml(writePyprojectToml)
.build();
}

Expand All @@ -133,6 +140,7 @@ static GeneratorConfiguration resolveGeneratorConfiguration(
.packageVersion(cliConfig.packageVersion())
.packageUrl(cliConfig.packageUrl())
.shouldWriteCondaRecipe(cliConfig.shouldWriteCondaRecipe())
.shouldWritePyprojectToml(cliConfig.shouldWritePyprojectToml())
.generateRawSource(cliConfig.generateRawSource())
.build();
}
Expand Down