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

Unified Diff: vpython/python/version.go

Issue 2874683002: vpython: Version parser from PEP440. (Closed)
Patch Set: 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 side-by-side diff with in-line comments
Download patch
Index: vpython/python/version.go
diff --git a/vpython/python/version.go b/vpython/python/version.go
index 5d4639773bfc0166487c5b0c0333fd833a3dfc6a..30b448b9019ae64a9174e6fec5889200f33a9838 100644
--- a/vpython/python/version.go
+++ b/vpython/python/version.go
@@ -6,13 +6,30 @@ package python
import (
"fmt"
+ "regexp"
"strconv"
"strings"
"github.com/luci/luci-go/common/errors"
)
+// canonicalVersionRE is a regular expression that can match canonical Python
+// versions.
+//
+// This has been modified from the PEP440 canonical regular expression to
+// exclude parts outside of the (major.minor.patch...) section.
+var canonicalVersionRE = regexp.MustCompile(
+ `^([1-9]\d*!)?` +
+ `((0|[1-9]\d*)(\.(0|[1-9]\d*))*)` +
+ `(((a|b|rc)(0|[1-9]\d*))?(\.post(0|[1-9]\d*))?(\.dev(0|[1-9]\d*))?)` +
+ `(\+.*)?$`)
+
// Version is a Python interpreter version.
+//
+// It is a simplified version of the Python interpreter version scheme defined
+// in PEP 440: https://www.python.org/dev/peps/pep-0440/
+//
+// Notably, it extracts the major, minor, and patch values out of the version.
type Version struct {
Major int
Minor int
@@ -21,10 +38,19 @@ type Version struct {
// ParseVersion parses a Python version from a version string (e.g., "1.2.3").
func ParseVersion(s string) (Version, error) {
- if len(s) == 0 {
- return Version{}, nil
+ var v Version
+ if s == "" {
+ return v, nil
}
+ match := canonicalVersionRE.FindStringSubmatch(s)
+ if match == nil {
+ return v, errors.Reason("non-canonical Python version string: %(value)q").
+ D("value", s).
+ Err()
+ }
+ parts := strings.Split(match[2], ".")
+
parseVersion := func(value string) (int, error) {
version, err := strconv.Atoi(value)
if err != nil {
@@ -32,44 +58,29 @@ func ParseVersion(s string) (Version, error) {
D("value", value).
Err()
}
- if version < 0 {
- return 0, errors.Reason("version (%(version)d) must not be negative").
- D("version", version).
- Err()
- }
return version, nil
}
- var v Version
- parts := strings.Split(s, ".")
+ // Regexp match guarantees that "parts" will have at least one component, and
+ // that all components are well-formed numbers.
var err error
- switch l := len(parts); l {
- case 3:
+ if len(parts) >= 3 {
if v.Patch, err = parseVersion(parts[2]); err != nil {
return v, errors.Annotate(err).Reason("invalid patch value").Err()
}
- fallthrough
-
- case 2:
+ }
+ if len(parts) >= 2 {
if v.Minor, err = parseVersion(parts[1]); err != nil {
return v, errors.Annotate(err).Reason("invalid minor value").Err()
}
- fallthrough
-
- case 1:
- if v.Major, err = parseVersion(parts[0]); err != nil {
- return v, errors.Annotate(err).Reason("invalid major value").Err()
- }
- if v.IsZero() {
- return v, errors.Reason("version is incomplete").Err()
- }
- return v, nil
-
- default:
- return v, errors.Reason("unsupported number of parts (%(count)d)").
- D("count", l).
- Err()
}
+ if v.Major, err = parseVersion(parts[0]); err != nil {
+ return v, errors.Annotate(err).Reason("invalid major value").Err()
+ }
+ if v.IsZero() {
+ return v, errors.Reason("version is incomplete").Err()
+ }
+ return v, nil
}
func (v Version) String() string {

Powered by Google App Engine
This is Rietveld 408576698