Skip to content

Commit

Permalink
Use exec.d to set PYTHONPYCACHEPREFIX
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryan Moran authored and robdimsdale committed Jun 17, 2022
1 parent 36f7178 commit ef29681
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 24 deletions.
2 changes: 1 addition & 1 deletion build.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func Build(dependencies DependencyManager, sbomGenerator SBOMGenerator, logger s
}

cpythonLayer.SharedEnv.Default("PYTHONPATH", cpythonLayer.Path)
cpythonLayer.SharedEnv.Default("PYTHONPYCACHEPREFIX", "$HOME/.pycache")
cpythonLayer.ExecD = []string{filepath.Join(context.CNBPath, "bin", "env")}

logger.Break()

Expand Down
7 changes: 5 additions & 2 deletions build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,7 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
Expect(layer.Path).To(Equal(filepath.Join(layersDir, "cpython")))

Expect(layer.SharedEnv).To(Equal(packit.Environment{
"PYTHONPATH.default": filepath.Join(layersDir, "cpython"),
"PYTHONPYCACHEPREFIX.default": "$HOME/.pycache",
"PYTHONPATH.default": filepath.Join(layersDir, "cpython"),
}))
Expect(layer.BuildEnv).To(BeEmpty())
Expect(layer.LaunchEnv).To(BeEmpty())
Expand All @@ -132,6 +131,10 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
"dependency-sha": "python-dependency-sha",
}))

Expect(layer.ExecD).To(Equal([]string{
filepath.Join(cnbDir, "bin", "env"),
}))

Expect(layer.SBOM.Formats()).To(Equal([]packit.SBOMFormat{
{
Extension: sbom.Format(sbom.CycloneDXFormat).Extension(),
Expand Down
2 changes: 1 addition & 1 deletion buildpack.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ api = "0.7"
uri = "https://github.com/paketo-buildpacks/cpython/blob/main/LICENSE"

[metadata]
include-files = ["bin/run", "bin/build", "bin/detect", "buildpack.toml"]
include-files = ["bin/run", "bin/build", "bin/detect", "bin/env", "buildpack.toml"]
pre-package = "./scripts/build.sh"
[metadata.default-versions]
python = "3.9.x"
Expand Down
14 changes: 14 additions & 0 deletions cmd/env/internal/init_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package internal_test

import (
"testing"

"github.com/sclevine/spec"
"github.com/sclevine/spec/report"
)

func TestUnitEnv(t *testing.T) {
suite := spec.New("cmd/env/internal", spec.Report(report.Terminal{}))
suite("Run", testRun)
suite.Run(t)
}
36 changes: 36 additions & 0 deletions cmd/env/internal/run.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package internal

import (
"io"
"path/filepath"
"strings"

"github.com/BurntSushi/toml"
)

// Run will write a TOML environment definition to the output io.Writer with
// the following rules:
// 1. If $PYTHONPYCACHEPREFIX is set, do nothing
// 2. If $PYTHONPYCACHEPREFIX is unset, set it to the expanded value of
// $HOME/.pycache
func Run(env []string, output io.Writer) error {
var home string
for _, v := range env {
if strings.HasPrefix(v, "HOME=") {
home = strings.TrimPrefix(v, "HOME=")
}

if strings.HasPrefix(v, "PYTHONPYCACHEPREFIX=") {
return nil
}
}

err := toml.NewEncoder(output).Encode(map[string]string{
"PYTHONPYCACHEPREFIX": filepath.Join(home, ".pycache"),
})
if err != nil {
return err
}

return nil
}
61 changes: 61 additions & 0 deletions cmd/env/internal/run_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package internal_test

import (
"bytes"
"os"
"testing"

"github.com/paketo-buildpacks/cpython/cmd/env/internal"
"github.com/sclevine/spec"

. "github.com/onsi/gomega"
. "github.com/paketo-buildpacks/packit/v2/matchers"
)

func testRun(t *testing.T, context spec.G, it spec.S) {
var Expect = NewWithT(t).Expect

context("when $PYTHONPYCACHEPREFIX is not set", func() {
it("sets it to $HOME/.pycache", func() {
buffer := bytes.NewBuffer(nil)
err := internal.Run([]string{"HOME=/some-home"}, buffer)
Expect(err).NotTo(HaveOccurred())

Expect(buffer.String()).To(MatchTOML(`
PYTHONPYCACHEPREFIX = "/some-home/.pycache"
`))
})
})

context("when $PYTHONPYCACHEPREFIX is already set", func() {
it("preserves the existing value", func() {
buffer := bytes.NewBuffer(nil)
err := internal.Run([]string{
"HOME=/some-home",
"PYTHONPYCACHEPREFIX=/other-home/.pycache",
}, buffer)
Expect(err).NotTo(HaveOccurred())

Expect(buffer.String()).To(BeEmpty())
})
})

context("failure cases", func() {
context("when the output cannot be written to", func() {
var file *os.File

it.Before(func() {
file, err := os.CreateTemp("", "")
Expect(err).NotTo(HaveOccurred())

Expect(file.Close()).To(Succeed())
Expect(os.Remove(file.Name())).To(Succeed())
})

it("returns an error", func() {
err := internal.Run([]string{"HOME=/some-home"}, file)
Expect(err).To(MatchError("invalid argument"))
})
})
})
}
16 changes: 16 additions & 0 deletions cmd/env/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

import (
"log"
"os"

"github.com/paketo-buildpacks/cpython/cmd/env/internal"
)

// env will set environment variables that are dynamically defined at runtime
func main() {
err := internal.Run(os.Environ(), os.NewFile(3, "/dev/fd/3"))
if err != nil {
log.Fatal(err)
}
}
17 changes: 8 additions & 9 deletions integration/default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ func testDefault(t *testing.T, context spec.G, it spec.S) {
Expect(err).ToNot(HaveOccurred())

Eventually(container).Should(BeAvailable())
Eventually(container).Should(Serve(ContainSubstring("hello world")).OnPort(8080))
Eventually(container).Should(Serve(SatisfyAll(
ContainSubstring("hello world"),
ContainSubstring("PYTHONPYCACHEPREFIX=/home/cnb/.pycache"),
)).OnPort(8080))

Expect(logs).To(ContainLines(
MatchRegexp(fmt.Sprintf(`%s \d+\.\d+\.\d+`, buildpackInfo.Buildpack.Name)),
Expand All @@ -84,12 +87,10 @@ func testDefault(t *testing.T, context spec.G, it spec.S) {
))
Expect(logs).To(ContainLines(
" Configuring build environment",
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
` PYTHONPYCACHEPREFIX -> "$HOME/.pycache"`,
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
"",
" Configuring launch environment",
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
` PYTHONPYCACHEPREFIX -> "$HOME/.pycache"`,
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
))
})

Expand Down Expand Up @@ -218,12 +219,10 @@ func testDefault(t *testing.T, context spec.G, it spec.S) {

Expect(logs).To(ContainLines(
" Configuring build environment",
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
` PYTHONPYCACHEPREFIX -> "$HOME/.pycache"`,
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
"",
" Configuring launch environment",
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
` PYTHONPYCACHEPREFIX -> "$HOME/.pycache"`,
fmt.Sprintf(` PYTHONPATH -> "/layers/%s/cpython"`, strings.ReplaceAll(buildpackInfo.Buildpack.ID, "/", "_")),
))
})
})
Expand Down
3 changes: 1 addition & 2 deletions integration/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func TestIntegration(t *testing.T) {
// Do not truncate Gomega matcher output
// The buildpack output text can be large and we often want to see all of it.
format.MaxLength = 0
SetDefaultEventuallyTimeout(30 * time.Second)

Expect := NewWithT(t).Expect

Expand Down Expand Up @@ -83,8 +84,6 @@ func TestIntegration(t *testing.T) {
Execute(settings.Config.BuildPlan)
Expect(err).NotTo(HaveOccurred())

SetDefaultEventuallyTimeout(5 * time.Second)

suite := spec.New("Integration", spec.Report(report.Terminal{}), spec.Parallel())
suite("Default", testDefault)
suite("Offline", testOffline)
Expand Down
21 changes: 12 additions & 9 deletions integration/testdata/default_app/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@

class Server(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type","text/plain")
self.end_headers()
self.wfile.write(bytes("hello world", "utf8"))
return
self.send_response(200)
self.send_header("Content-type","text/plain")
self.end_headers()

self.wfile.write(bytes("hello world", "utf8"))

prefix = os.getenv("PYTHONPYCACHEPREFIX")
self.wfile.write(bytes(f'PYTHONPYCACHEPREFIX={prefix}', "utf8"))

def do_HEAD(self):
self.send_response(200)
self.send_header("Content-type","text/plain")
self.end_headers()
return
self.send_response(200)
self.send_header("Content-type","text/plain")
self.end_headers()

port = int(os.getenv("PORT", "8080"))
server_address = ("", port)
Expand Down

0 comments on commit ef29681

Please sign in to comment.