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

Side by Side Diff: vpython/python/version.go

Issue 2874683002: vpython: Version parser from PEP440. (Closed)
Patch Set: stronger test case Created 3 years, 7 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 | « vpython/python/python_test.go ('k') | vpython/python/version_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2017 The LUCI Authors. All rights reserved. 1 // Copyright 2017 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0 2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file. 3 // that can be found in the LICENSE file.
4 4
5 package python 5 package python
6 6
7 import ( 7 import (
8 "fmt" 8 "fmt"
9 "regexp"
9 "strconv" 10 "strconv"
10 "strings" 11 "strings"
11 12
12 "github.com/luci/luci-go/common/errors" 13 "github.com/luci/luci-go/common/errors"
13 ) 14 )
14 15
16 // canonicalVersionRE is a regular expression that can match canonical Python
17 // versions.
18 //
19 // This has been modified from the PEP440 canonical regular expression to
20 // exclude parts outside of the (major.minor.patch...) section.
21 var canonicalVersionRE = regexp.MustCompile(
22 `^([1-9]\d*!)?` +
23 `((0|[1-9]\d*)(\.(0|[1-9]\d*))*)` +
24 `(((a|b|rc)(0|[1-9]\d*))?(\.post(0|[1-9]\d*))?(\.dev(0|[1-9]\d*) )?)` +
25 `(\+.*)?$`)
26
15 // Version is a Python interpreter version. 27 // Version is a Python interpreter version.
28 //
29 // It is a simplified version of the Python interpreter version scheme defined
30 // in PEP 440: https://www.python.org/dev/peps/pep-0440/
31 //
32 // Notably, it extracts the major, minor, and patch values out of the version.
16 type Version struct { 33 type Version struct {
17 Major int 34 Major int
18 Minor int 35 Minor int
19 Patch int 36 Patch int
20 } 37 }
21 38
22 // ParseVersion parses a Python version from a version string (e.g., "1.2.3"). 39 // ParseVersion parses a Python version from a version string (e.g., "1.2.3").
23 func ParseVersion(s string) (Version, error) { 40 func ParseVersion(s string) (Version, error) {
24 » if len(s) == 0 { 41 » var v Version
25 » » return Version{}, nil 42 » if s == "" {
43 » » return v, nil
26 } 44 }
27 45
46 match := canonicalVersionRE.FindStringSubmatch(s)
47 if match == nil {
48 return v, errors.Reason("non-canonical Python version string: %( value)q").
49 D("value", s).
50 Err()
51 }
52 parts := strings.Split(match[2], ".")
53
28 parseVersion := func(value string) (int, error) { 54 parseVersion := func(value string) (int, error) {
29 version, err := strconv.Atoi(value) 55 version, err := strconv.Atoi(value)
30 if err != nil { 56 if err != nil {
31 return 0, errors.Annotate(err).Reason("invalid number va lue: %(value)q"). 57 return 0, errors.Annotate(err).Reason("invalid number va lue: %(value)q").
32 D("value", value). 58 D("value", value).
33 Err() 59 Err()
34 } 60 }
35 if version < 0 {
36 return 0, errors.Reason("version (%(version)d) must not be negative").
37 D("version", version).
38 Err()
39 }
40 return version, nil 61 return version, nil
41 } 62 }
42 63
43 » var v Version 64 » // Regexp match guarantees that "parts" will have at least one component , and
44 » parts := strings.Split(s, ".") 65 » // that all components are well-formed numbers.
45 var err error 66 var err error
46 » switch l := len(parts); l { 67 » if len(parts) >= 3 {
47 » case 3:
48 if v.Patch, err = parseVersion(parts[2]); err != nil { 68 if v.Patch, err = parseVersion(parts[2]); err != nil {
49 return v, errors.Annotate(err).Reason("invalid patch val ue").Err() 69 return v, errors.Annotate(err).Reason("invalid patch val ue").Err()
50 } 70 }
51 » » fallthrough 71 » }
52 72 » if len(parts) >= 2 {
53 » case 2:
54 if v.Minor, err = parseVersion(parts[1]); err != nil { 73 if v.Minor, err = parseVersion(parts[1]); err != nil {
55 return v, errors.Annotate(err).Reason("invalid minor val ue").Err() 74 return v, errors.Annotate(err).Reason("invalid minor val ue").Err()
56 } 75 }
57 fallthrough
58
59 case 1:
60 if v.Major, err = parseVersion(parts[0]); err != nil {
61 return v, errors.Annotate(err).Reason("invalid major val ue").Err()
62 }
63 if v.IsZero() {
64 return v, errors.Reason("version is incomplete").Err()
65 }
66 return v, nil
67
68 default:
69 return v, errors.Reason("unsupported number of parts (%(count)d) ").
70 D("count", l).
71 Err()
72 } 76 }
77 if v.Major, err = parseVersion(parts[0]); err != nil {
78 return v, errors.Annotate(err).Reason("invalid major value").Err ()
79 }
80 if v.IsZero() {
81 return v, errors.Reason("version is incomplete").Err()
82 }
83 return v, nil
73 } 84 }
74 85
75 func (v Version) String() string { 86 func (v Version) String() string {
76 if v.IsZero() { 87 if v.IsZero() {
77 return "" 88 return ""
78 } 89 }
79 return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) 90 return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
80 } 91 }
81 92
82 // IsZero returns true if the Version is empty. This is true if the Major field, 93 // IsZero returns true if the Version is empty. This is true if the Major field,
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
135 case v.Major > other.Major: 146 case v.Major > other.Major:
136 return false 147 return false
137 case v.Minor < other.Minor: 148 case v.Minor < other.Minor:
138 return true 149 return true
139 case v.Minor > other.Minor: 150 case v.Minor > other.Minor:
140 return false 151 return false
141 default: 152 default:
142 return (v.Patch < other.Patch) 153 return (v.Patch < other.Patch)
143 } 154 }
144 } 155 }
OLDNEW
« no previous file with comments | « vpython/python/python_test.go ('k') | vpython/python/version_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698