Skip to content
This repository has been archived by the owner on Jun 28, 2022. It is now read-only.

Python: Introduced versioned api directory exporting types. #1368

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import com.google.api.codegen.transformer.php.PhpGapicSurfaceTransformer;
import com.google.api.codegen.transformer.php.PhpPackageMetadataTransformer;
import com.google.api.codegen.transformer.py.PythonGapicSurfaceTestTransformer;
import com.google.api.codegen.transformer.py.PythonGapicSurfaceTransformer;
import com.google.api.codegen.transformer.py.PythonPackageMetadataTransformer;
import com.google.api.codegen.transformer.ruby.RubyGapicSurfaceDocTransformer;
import com.google.api.codegen.transformer.ruby.RubyGapicSurfaceTestTransformer;
Expand Down Expand Up @@ -355,6 +356,13 @@ public static List<GapicProvider<? extends Object>> defaultCreate(
.setSnippetFileNames(Arrays.asList("py/main.snip"))
.setCodePathMapper(pythonPathMapper)
.build();
GapicProvider<? extends Object> surfaceProvider =
ViewModelGapicProvider.newBuilder()
.setModel(model)
.setProductConfig(productConfig)
.setSnippetSetRunner(new CommonSnippetSetRunner(new CommonRenderingUtil()))
.setModelToViewTransformer(new PythonGapicSurfaceTransformer(packageConfig))
.build();
// Note: enumProvider implementation doesn't care about the InputElementT view.
GapicProvider<? extends Object> enumProvider =
CommonGapicProvider.<Interface>newBuilder()
Expand All @@ -381,6 +389,7 @@ public static List<GapicProvider<? extends Object>> defaultCreate(
.build();
providers.add(mainProvider);
providers.add(clientConfigProvider);
providers.add(surfaceProvider);
providers.add(enumProvider);

if (id.equals(PYTHON_DOC)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public GrpcStubView generateGrpcStub(

stub.name(namer.getStubName(targetInterface));
stub.fullyQualifiedType(namer.getFullyQualifiedStubType(targetInterface));
stub.type(namer.getStubType(targetInterface));
stub.createStubFunctionName(namer.getCreateStubFunctionName(targetInterface));
String grpcClientTypeName =
namer.getAndSaveNicknameForGrpcClientTypeName(context.getModelTypeTable(), targetInterface);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,20 @@ public String getNamespace(Interface apiInterface) {
return qualifiedName(namePath.withoutHead());
}

/** The namespace of the package. */

This comment was marked as spam.

This comment was marked as spam.

public String getPackageNamespace(String version) {
NamePath namePath = typeNameConverter.getNamePath(packageName);
if (namePath.getHead().equals(version)) {
return qualifiedName(namePath.withoutHead());
}
return qualifiedName(namePath);
}

/** The versioned namespace of an api. Example: google.cloud.vision_v1 */
public String getVersionedDirectoryNamespace(Interface apiInterface) {
return getNotImplementedString("SurfaceNamer.getVersionedDirectoryNamespace");
}

/** The modules of the package. */
public ImmutableList<String> getApiModules() {
return ImmutableList.<String>of();
Expand Down Expand Up @@ -475,6 +489,15 @@ public String getByteLengthFunctionName(TypeRef typeRef) {
return getNotImplementedString("SurfaceNamer.getByteLengthFunctionName");
}

/** The name of the class that ties in the helper functions to a client partial veneers. */
public String getHelpersClassName(String apiShortName) {
return publicClassName(Name.anyCamel(apiShortName).join("helpers"));
}

/** The name of a stub type of an interface */
public String getStubType(Interface apiInterface) {

This comment was marked as spam.

This comment was marked as spam.

return publicClassName(Name.upperCamel(apiInterface.getSimpleName(), "Stub"));
}
/////////////////////////////////////// Variable names //////////////////////////////////////////

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/* Copyright 2017 Google Inc
*
* 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.google.api.codegen.transformer.py;

import com.google.api.codegen.InterfaceView;
import com.google.api.codegen.TargetLanguage;
import com.google.api.codegen.config.GapicProductConfig;
import com.google.api.codegen.config.PackageMetadataConfig;
import com.google.api.codegen.transformer.FileHeaderTransformer;
import com.google.api.codegen.transformer.ModelToViewTransformer;
import com.google.api.codegen.transformer.ModelTypeTable;
import com.google.api.codegen.transformer.SurfaceNamer;
import com.google.api.codegen.util.py.PythonTypeTable;
import com.google.api.codegen.viewmodel.ImportSectionView;
import com.google.api.codegen.viewmodel.ViewModel;
import com.google.api.codegen.viewmodel.metadata.VersionIndexRequireView;
import com.google.api.codegen.viewmodel.metadata.VersionIndexView;
import com.google.api.tools.framework.model.Interface;
import com.google.api.tools.framework.model.Model;
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.util.List;

public class PythonGapicSurfaceTransformer implements ModelToViewTransformer {

private static final String TYPES_TEMPLEATE_FILE = "py/types.snip";
private static final String VERSIONED_INIT_TEMPLATE_FILE =
"py/versioned_directory__init__.py.snip";

private static final PythonImportSectionTransformer importSectionTransformer =
new PythonImportSectionTransformer();
private static final FileHeaderTransformer fileHeaderTransformer =
new FileHeaderTransformer(importSectionTransformer);

private final PackageMetadataConfig packageConfig;

public PythonGapicSurfaceTransformer(PackageMetadataConfig packageConfig) {
this.packageConfig = packageConfig;
}

@Override
public List<ViewModel> transform(Model model, GapicProductConfig productConfig) {
ImmutableList.Builder<ViewModel> views = ImmutableList.builder();
views.addAll(generateVersionedDirectoryViews(model, productConfig));
return views.build();
}

@Override
public List<String> getTemplateFileNames() {
return ImmutableList.of(TYPES_TEMPLEATE_FILE, VERSIONED_INIT_TEMPLATE_FILE);
}

private List<ViewModel> generateVersionedDirectoryViews(
Model model, GapicProductConfig productConfig) {
ImmutableList.Builder<ViewModel> views = ImmutableList.builder();
views.add(generateTypesView(model, productConfig));
views.add(generateVersionedInitView(model, productConfig));
return views.build();
}

private ViewModel generateTypesView(Model model, GapicProductConfig productConfig) {
SurfaceNamer namer = new PythonSurfaceNamer(productConfig.getPackageName());
ImportSectionView imports =
importSectionTransformer.generateTypesImportSection(model, productConfig, namer);
Iterable<Interface> apiInterfaces = new InterfaceView().getElementIterable(model);
return VersionIndexView.newBuilder()
.templateFileName(TYPES_TEMPLEATE_FILE)
.outputPath(typesOutputFile(model, namer))
.requireViews(ImmutableList.<VersionIndexRequireView>of())
.apiVersion(namer.getApiWrapperModuleVersion())
.namespace(namer.getVersionedDirectoryNamespace(apiInterfaces.iterator().next()))
.packageVersion(packageConfig.generatedPackageVersionBound(TargetLanguage.PYTHON).lower())
.fileHeader(fileHeaderTransformer.generateFileHeader(productConfig, imports, namer))
.build();
}

private ViewModel generateVersionedInitView(Model model, GapicProductConfig productConfig) {
SurfaceNamer namer = new PythonSurfaceNamer(productConfig.getPackageName());
ImportSectionView imports =
importSectionTransformer.generateVersionedInitImportSection(
model, productConfig, packageConfig, namer);
Iterable<Interface> apiInterfaces = new InterfaceView().getElementIterable(model);
return VersionIndexView.newBuilder()
.templateFileName(VERSIONED_INIT_TEMPLATE_FILE)
.outputPath(versionedInitOutputFile(model, namer))
.requireViews(versionedInitRequireViews(model, productConfig, namer))
.apiVersion(namer.getApiWrapperModuleVersion())
.namespace(namer.getVersionedDirectoryNamespace(apiInterfaces.iterator().next()))
.packageVersion(packageConfig.generatedPackageVersionBound(TargetLanguage.PYTHON).lower())
.fileHeader(fileHeaderTransformer.generateFileHeader(productConfig, imports, namer))
.build();
}

private List<VersionIndexRequireView> versionedInitRequireViews(
Model model, GapicProductConfig productConfig, SurfaceNamer namer) {
ImmutableList.Builder<VersionIndexRequireView> views = ImmutableList.builder();
Iterable<Interface> apiInterfaces = new InterfaceView().getElementIterable(model);
ModelTypeTable typeTable = emptyTypeTable(productConfig);
for (Interface apiInterface : apiInterfaces) {
views.add(
VersionIndexRequireView.newBuilder()
.clientName(namer.getAndSaveNicknameForGrpcClientTypeName(typeTable, apiInterface))
.localName(
namer.getApiWrapperClassName(productConfig.getInterfaceConfig(apiInterface)))
.fileName(namer.getNotImplementedString("VersionIndexRequireView.fileName"))
.helpersClassName(namer.getHelpersClassName(packageConfig.shortName()))
.build());
}
return views.build();
}

private ModelTypeTable emptyTypeTable(GapicProductConfig productConfig) {
return new ModelTypeTable(
new PythonTypeTable(productConfig.getPackageName()),
new PythonModelTypeNameConverter(productConfig.getPackageName()));
}

private String typesOutputFile(Model model, SurfaceNamer namer) {
return versionedDirectoryPath(model, namer) + File.separator + "types.py";
}

private String versionedInitOutputFile(Model model, SurfaceNamer namer) {
return versionedDirectoryPath(model, namer) + File.separator + "__init__.py";
}

private String versionedDirectoryPath(Model model, SurfaceNamer namer) {
Iterable<Interface> apiInterfaces = new InterfaceView().getElementIterable(model);
String namespace = namer.getVersionedDirectoryNamespace(apiInterfaces.iterator().next());
return namespace.replace(".", File.separator);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,34 @@
*/
package com.google.api.codegen.transformer.py;

import com.google.api.codegen.InterfaceView;
import com.google.api.codegen.config.GapicProductConfig;
import com.google.api.codegen.config.PackageMetadataConfig;
import com.google.api.codegen.metacode.InitCodeNode;
import com.google.api.codegen.transformer.GapicInterfaceContext;
import com.google.api.codegen.transformer.GapicMethodContext;
import com.google.api.codegen.transformer.ImportSectionTransformer;
import com.google.api.codegen.transformer.InterfaceContext;
import com.google.api.codegen.transformer.ModelTypeTable;
import com.google.api.codegen.transformer.SurfaceNamer;
import com.google.api.codegen.util.TypeAlias;
import com.google.api.codegen.util.py.PythonTypeTable;
import com.google.api.codegen.viewmodel.ImportFileView;
import com.google.api.codegen.viewmodel.ImportSectionView;
import com.google.api.codegen.viewmodel.ImportTypeView;
import com.google.api.tools.framework.model.Interface;
import com.google.api.tools.framework.model.MessageType;
import com.google.api.tools.framework.model.Model;
import com.google.api.tools.framework.model.ProtoFile;
import com.google.api.tools.framework.model.TypeRef;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;

Expand Down Expand Up @@ -65,6 +77,26 @@ public ImportSectionView generateSmokeTestImportSection(
.build();
}

public ImportSectionView generateTypesImportSection(
Model model, GapicProductConfig productConfig, SurfaceNamer namer) {
return ImportSectionView.newBuilder()
.appImports(generateTypesProtoImports(model, productConfig, namer))
.externalImports(generateTypesExternalImports())
.standardImports(generateTypesStandardImports())
.build();
}

public ImportSectionView generateVersionedInitImportSection(
Model model,
GapicProductConfig productConfig,
PackageMetadataConfig packageConfig,
SurfaceNamer namer) {
return ImportSectionView.newBuilder()
.appImports(generateVersionedInitAppImports(model, productConfig, packageConfig, namer))
.standardImports(generateVersionedInitStandardImports())
.build();
}

private List<ImportFileView> generateInitCodeAppImports(
GapicMethodContext context, Iterable<InitCodeNode> specItemNodes) {
return ImmutableList.<ImportFileView>builder()
Expand Down Expand Up @@ -151,6 +183,101 @@ private List<ImportFileView> generateTestAppImports(GapicInterfaceContext contex
return new ArrayList<>(appImports);
}

private List<ImportFileView> generateTypesProtoImports(
Model model, GapicProductConfig productConfig, SurfaceNamer namer) {
ModelTypeTable typeTable = emptyTypeTable(productConfig);
Set<ImportFileView> imports = new TreeSet<>(importFileViewComparator());

// Save proto file import names to the type table for disambiguation.
Set<ProtoFile> protoFileDependencies = getProtofileDependencies(model);
populateTypeTable(protoFileDependencies, typeTable);

// Get disambiguated imports.
for (Map.Entry<String, TypeAlias> entry : typeTable.getImports().entrySet()) {
imports.add(generateAppImport(entry.getKey(), entry.getValue().getNickname()));
}
return ImmutableList.<ImportFileView>builder().addAll(imports).build();
}

private ModelTypeTable emptyTypeTable(GapicProductConfig productConfig) {
return new ModelTypeTable(
new PythonTypeTable(productConfig.getPackageName()),
new PythonModelTypeNameConverter(productConfig.getPackageName()));
}

private List<ImportFileView> generateTypesExternalImports() {
return ImmutableList.of(createImport("google.gax.utils.messages", "get_messages"));
}

private List<ImportFileView> generateTypesStandardImports() {
return ImmutableList.of(createImport("__future__", "absoulute_import"), createImport("sys"));
}

private void populateTypeTable(Set<ProtoFile> protoFileDependencies, ModelTypeTable typeTable) {
for (ProtoFile protoFile : protoFileDependencies) {
// For python, adding a single message from the proto file to the type table will populate
// the type table with the correct imports.
ImmutableList<MessageType> messages = protoFile.getMessages();
if (!messages.isEmpty()) {
typeTable.getAndSaveNicknameFor(TypeRef.of(messages.get(0)));
}
}
}

private Set<ProtoFile> getProtofileDependencies(Model model) {

This comment was marked as spam.

This comment was marked as spam.

// Set up BFS.
Iterable<Interface> apiInterfaces = new InterfaceView().getElementIterable(model);
Queue<ProtoFile> protoFileQueue = new LinkedList<>();
for (Interface apiInterface : apiInterfaces) {
protoFileQueue.add(apiInterface.getFile());
}

// BFS to get all proto dependencies.
Set<ProtoFile> seenFiles = new HashSet<>();
while (!protoFileQueue.isEmpty()) {
ProtoFile current = protoFileQueue.remove();
seenFiles.add(current);
for (ProtoFile protoFile : current.getDependencies()) {
if (!seenFiles.contains(protoFile)) {
protoFileQueue.add(protoFile);
}
}
}
return seenFiles;
}

private List<ImportFileView> generateVersionedInitStandardImports() {
return ImmutableList.of(createImport("__future__", "absoulte_import"));
}

private List<ImportFileView> generateVersionedInitAppImports(
Model model,
GapicProductConfig productConfig,
PackageMetadataConfig packageConfig,
SurfaceNamer namer) {

Iterable<Interface> apiInterfaces = new InterfaceView().getElementIterable(model);
ModelTypeTable typeTable = emptyTypeTable(productConfig);
for (Interface apiInterface : apiInterfaces) {
namer.getAndSaveNicknameForGrpcClientTypeName(typeTable, apiInterface);
}

Set<ImportFileView> imports = new TreeSet<>(importFileViewComparator());
// TODO: (landrito) Make this only generate the enums module if there are enums to export.

This comment was marked as spam.

This comment was marked as spam.

imports.add(createImport(productConfig.getPackageName(), "enums"));
String packageNamespace = namer.getPackageNamespace(packageConfig.apiVersion());
imports.add(
createImport(
packageNamespace + ".helpers", namer.getHelpersClassName(packageConfig.shortName())));
imports.add(
createImport(
namer.getVersionedDirectoryNamespace(apiInterfaces.iterator().next()), "types"));
for (Map.Entry<String, TypeAlias> entry : typeTable.getImports().entrySet()) {
imports.add(generateAppImport(entry.getKey(), entry.getValue().getNickname()));
}
return ImmutableList.<ImportFileView>builder().addAll(imports).build();
}

/**
* Orders the imports by:
*
Expand Down
Loading