Skip to content

Commit e00efac

Browse files
authored
feat(python): handle fully-handwritten libraries (#4248)
Changes generation to ignore libraries with no APIs, and changes migration to explicitly say they're veneers, and set the output directory. Later we *may* want to generate .repo-metadata.json files for fully-handwritten libraries, but for now we really don't need to (and that doesn't work). It's tempting to put the check into the language-agnostic code, but it's possible that some languages *will* want to generate some files even when there are no APIs.
1 parent 98420f0 commit e00efac

5 files changed

Lines changed: 73 additions & 12 deletions

File tree

internal/librarian/python/generate.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ package python
1717

1818
import (
1919
"context"
20-
"errors"
2120
"fmt"
2221
"os"
2322
"os/exec"
@@ -36,8 +35,6 @@ const (
3635
googleapisDevDocumentationTemplate = "https://googleapis.dev/python/%s/latest"
3736
)
3837

39-
var errNoApis = errors.New("no apis configured for library")
40-
4138
// GenerateLibraries generates all the given libraries in sequence.
4239
func GenerateLibraries(ctx context.Context, config *config.Config, libraries []*config.Library, googleapisDir string) error {
4340
for _, library := range libraries {
@@ -50,8 +47,9 @@ func GenerateLibraries(ctx context.Context, config *config.Config, libraries []*
5047

5148
// generate generates a Python client library.
5249
func generate(ctx context.Context, config *config.Config, library *config.Library, googleapisDir string) error {
50+
// If the library has no APIs, there's nothing to do.
5351
if len(library.APIs) == 0 {
54-
return fmt.Errorf("error generating %s: %w", library.Name, errNoApis)
52+
return nil
5553
}
5654

5755
// Convert library.Output to absolute path since protoc runs from a

internal/librarian/python/generate_test.go

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -633,10 +633,6 @@ func TestGenerateLibraries_Error(t *testing.T) {
633633
requireSynthtool(t)
634634
repoRoot := t.TempDir()
635635
createReplacementScripts(t, repoRoot)
636-
outdir, err := filepath.Abs(filepath.Join(repoRoot, "packages", "secretmanager"))
637-
if err != nil {
638-
t.Fatal(err)
639-
}
640636

641637
cfg := &config.Config{
642638
Language: serviceconfig.LangPython,
@@ -645,17 +641,47 @@ func TestGenerateLibraries_Error(t *testing.T) {
645641

646642
libraries := []*config.Library{
647643
{
648-
Name: "no-apis",
649-
Output: filepath.Join(outdir, "packages", "no-apis"),
644+
Name: "bad-output",
645+
Output: "/../bad-output",
646+
APIs: []*config.API{
647+
{
648+
Path: "google/cloud/configdelivery/v1",
649+
},
650+
},
650651
},
651652
}
652653
gotErr := GenerateLibraries(t.Context(), cfg, libraries, googleapisDir)
653-
wantErr := errNoApis
654+
wantErr := os.ErrPermission
654655
if !errors.Is(gotErr, wantErr) {
655656
t.Errorf("GenerateLibraries error = %v, wantErr %v", gotErr, wantErr)
656657
}
657658
}
658659

660+
// Note: this is separate to TestGenerate as there's so little that we want
661+
// to do here. Making TestGenerate table-driven in order to take two entirely
662+
// different paths doesn't feel useful.
663+
func TestGenerate_NoAPIs(t *testing.T) {
664+
repoRoot := t.TempDir()
665+
cfg := &config.Config{
666+
Language: serviceconfig.LangPython,
667+
Repo: "googleapis/google-cloud-python",
668+
}
669+
670+
library := &config.Library{
671+
Name: "no-apis",
672+
Output: filepath.Join(repoRoot, "packages", "will-not-be-created"),
673+
}
674+
if err := generate(t.Context(), cfg, library, googleapisDir); err != nil {
675+
t.Fatal(err)
676+
}
677+
// Validate that we haven't got as far as creating the output directory.
678+
_, gotErr := os.Stat(library.Output)
679+
wantErr := os.ErrNotExist
680+
if !errors.Is(gotErr, wantErr) {
681+
t.Errorf("Stat() error after generating with no APIs = %v, wantErr %v", gotErr, wantErr)
682+
}
683+
}
684+
659685
func TestGenerate(t *testing.T) {
660686
t.Parallel()
661687
if testing.Short() {

tool/cmd/migrate/python.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,11 @@ func buildPythonLibraries(input *MigrationInput, googleapisDir string) ([]*confi
8787
Version: libState.Version,
8888
Python: &config.PythonPackage{},
8989
}
90-
if libState.APIs != nil {
90+
if len(libState.APIs) > 0 {
9191
library.APIs = toAPIs(libState.APIs)
92+
} else {
93+
library.Output = filepath.Join("packages", library.Name)
94+
library.Veneer = true
9295
}
9396
// Convert "preserve" regexes into "keep" paths, sorted for ease
9497
// of testing.

tool/cmd/migrate/python_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,28 @@ func TestBuildPythonLibraries(t *testing.T) {
201201
},
202202
},
203203
},
204+
{
205+
name: "veneer",
206+
input: &MigrationInput{
207+
repoPath: "testdata/google-cloud-python",
208+
librarianState: &legacyconfig.LibrarianState{
209+
Libraries: []*legacyconfig.LibraryState{
210+
{
211+
ID: "google-api-core",
212+
},
213+
},
214+
},
215+
librarianConfig: &legacyconfig.LibrarianConfig{},
216+
},
217+
want: []*config.Library{
218+
{
219+
Name: "google-api-core",
220+
Veneer: true,
221+
Output: "packages/google-api-core",
222+
Python: &config.PythonPackage{NamePrettyOverride: "Google API client core library"},
223+
},
224+
},
225+
},
204226
} {
205227
t.Run(test.name, func(t *testing.T) {
206228
got, err := buildPythonLibraries(test.input, "testdata/googleapis")
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "google-api-core",
3+
"name_pretty": "Google API client core library",
4+
"client_documentation": "https://googleapis.dev/python/google-api-core/latest",
5+
"release_level": "stable",
6+
"language": "python",
7+
"library_type": "CORE",
8+
"repo": "googleapis/google-cloud-python",
9+
"distribution_name": "google-api-core",
10+
"default_version": "",
11+
"codeowner_team": "@googleapis/cloud-sdk-python-team"
12+
}

0 commit comments

Comments
 (0)