Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: vpython/wheel/wheel.go

Issue 2700273002: vpython: Add wheel parsing/management package. (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | vpython/wheel/wheel_test.go » ('j') | vpython/wheel/wheel_test.go » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2017 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file.
4
5 package wheel
6
7 import (
8 "fmt"
9 "os"
10 "path/filepath"
11 "strings"
12
13 "github.com/luci/luci-go/common/errors"
14 )
15
16 // Name is a parsed Python wheel name, defined here:
17 // https://www.python.org/dev/peps/pep-0427/#file-name-convention
18 //
19 // {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-\
20 // {platform tag}.whl .
21 type Name struct {
22 Distribution string
23 Version string
24 BuildTag string
25 PythonTag string
26 ABITag string
27 PlatformTag string
28 }
29
30 func (wn *Name) String() string {
31 return strings.Join([]string{
32 wn.Distribution,
33 wn.Version,
34 wn.BuildTag,
iannucci 2017/02/21 08:34:01 if BuildTag is empty, does this render correctly,
dnj 2017/02/21 18:19:07 It does not. Fixed.
35 wn.PythonTag,
36 wn.ABITag,
37 wn.PlatformTag,
38 }, "-") + ".whl"
39 }
40
41 // ParseName parses a wheel Name from its filename.
42 func ParseName(v string) (wn Name, err error) {
43 base := strings.TrimSuffix(v, ".whl")
44 if len(base) == len(v) {
45 err = errors.Reason("missing .whl suffix").Err()
46 return
47 }
48
49 skip := 0
50 switch parts := strings.Split(base, "-"); len(parts) {
51 case 6:
52 // Extra part: build tag.
53 wn.BuildTag = parts[2]
54 skip = 1
55 fallthrough
56
57 case 5:
58 wn.Distribution = parts[0]
59 wn.Version = parts[1]
60 wn.PythonTag = parts[2+skip]
61 wn.ABITag = parts[3+skip]
62 wn.PlatformTag = parts[4+skip]
63
64 default:
65 err = errors.Reason("unknown number of segments (%(segments)d)") .
66 D("segments", len(parts)).
67 Err()
68 return
69 }
70 return
71 }
72
73 // GlobFrom identifies all wheel files in the directory dir and returns their
74 // parsed wheel names.
iannucci 2017/02/21 08:34:02 note: non-recursively. Is this really glob-relate
dnj 2017/02/21 18:19:07 Done. Changed to "ScanDir".
75 func GlobFrom(dir string) ([]Name, error) {
76 globPattern := filepath.Join(dir, "*.whl")
77 matches, err := filepath.Glob(globPattern)
78 if err != nil {
79 return nil, errors.Annotate(err).Reason("failed to list wheel di rectory: %(dir)s").
80 D("dir", dir).
81 D("pattern", globPattern).
iannucci 2017/02/21 08:34:01 not in reason?
dnj 2017/02/21 18:19:07 Acknowledged.
82 Err()
83 }
84
85 names := make([]Name, 0, len(matches))
86 for _, match := range matches {
87 switch st, err := os.Stat(match); {
88 case err != nil:
89 return nil, errors.Annotate(err).Reason("failed to stat wheel: %(path)s").
90 D("path", match).
91 Err()
92
93 case st.IsDir():
94 // Ignore directories.
95 continue
96
97 default:
98 // A ".whl" file.
99 name := filepath.Base(match)
100 wheelName, err := ParseName(name)
101 if err != nil {
102 return nil, errors.Annotate(err).Reason("failed to parse wheel from: %(name)s").
103 D("name", name).
104 D("dir", dir).
iannucci 2017/02/21 08:34:01 in reason?
dnj 2017/02/21 18:19:07 Acknowledged.
105 Err()
106 }
107 names = append(names, wheelName)
108 }
109 }
110 return names, nil
111 }
112
113 // WriteRequirementsFile writes a valid "requirements.txt"-style pip reuirements
iannucci 2017/02/21 08:34:01 requirements
dnj 2017/02/21 18:19:07 Done.
114 // file containing the supplied wheels.
115 //
116 // The generated requirements will request the exact wheel version.
iannucci 2017/02/21 08:34:02 "exact wheel version" meaning a hash? Or the appro
dnj 2017/02/21 18:19:07 semver "==", clarified.
117 func WriteRequirementsFile(path string, wheels []Name) error {
118 fd, err := os.Create(path)
119 if err != nil {
120 return errors.Annotate(err).Reason("failed to create requirement s file").Err()
121 }
iannucci 2017/02/21 08:34:01 why not defer close?
dnj 2017/02/21 18:19:07 meh didn't want to use awkward defer error assignm
122
123 // Emit a series of "Distribution==Version" strings.
124 seen := make(map[Name]struct{}, len(wheels))
125 for _, wheel := range wheels {
126 // Only mention a given Distribution/Version once.
127 archetype := Name{
128 Distribution: wheel.Distribution,
129 Version: wheel.Version,
130 }
131 if _, ok := seen[archetype]; ok {
132 // Already seen a package for this archetype, skip it.
133 continue
134 }
135 seen[archetype] = struct{}{}
136
137 if _, err := fmt.Fprintf(fd, "%s==%s\n", archetype.Distribution, archetype.Version); err != nil {
138 fd.Close()
139 return errors.Annotate(err).Reason("failed to write to r equirements file").Err()
140 }
141 }
142
143 if err := fd.Close(); err != nil {
144 return errors.Annotate(err).Reason("failed to Close").Err()
145 }
146 return nil
147 }
OLDNEW
« no previous file with comments | « no previous file | vpython/wheel/wheel_test.go » ('j') | vpython/wheel/wheel_test.go » ('J')

Powered by Google App Engine
This is Rietveld 408576698