A Path to v2.0#482
Conversation
Run Go's own flag package test suite against pflag v2 (dot-imported as "flag") to keep the drop-in-replacement promise honest. The suite is the spec for v2: as the package is currently empty, every failure is an implementation gap to close (see v2/conformance/README.md for the gap list / TODO). The suite is build-tagged "conformance" so it does not affect the normal `go test ./...` run, and is Go-version aware: a separate copy of upstream flag_test.go is vendored per minor version, each pinned with a build constraint (e.g. go1.26 && !go1.27), so the CI matrix tests each supported Go release against that release's own flag tests and upstream changes surface as diffs. hack/sync.sh regenerates the copies (also wired to `go generate ./conformance/...`); a guard test fails loudly if the running toolchain has no vendored copy. Adds a non-blocking conformance-v2 CI job (continue-on-error until v2 is complete). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the second conformance promise (POSIX/GNU argument syntax) alongside the vendored stdlib flag suite, plus the machinery to reconcile the two when they conflict. - gnuposix_test.go: hand-written suite keyed to the POSIX Utility Syntax Guidelines (1-14) and the GNU long-option extensions. No reusable Go-native POSIX corpus exists to vendor, so each case cites the rule it checks. - divergences.json: single source of truth cataloguing the intentional differences between pflag and the stdlib flag package. POSIX wins, so the listed stdlib tests are *expected* to fail; each entry names them and the rule that overrides them. Categories: posix-overrides-stdlib, pflag-design-differs, pflag-omits-gnu-feature, not-implemented-yet. - hack/oracle: reads `go test -json` and gates CI on the catalogue rather than raw pass/fail: green iff failures match the documented set exactly (catches regressions and silently-resolved divergences). The top-level "status" field tolerates a build failure while "bootstrapping" so CI is green during v2 build-out; "not-implemented-yet" entries are lenient (tolerated when failing, retirable when they start passing). - internal/divergence: catalogue types + parsing, shared by the manifest test and the oracle (no build tag, no v2 dependency). CI conformance-v2 job now pipes the suite through the oracle and drops continue-on-error; it is green now (bootstrapping) and tightens to enforcing once v2 compiles. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Hey @tomasaschan! As the project starts to move in the direction of a new major version, I would like to become a bit more involved in this transition. I've been a heavy user of pflag internally at Siemens for a while now, and there are aspects of the design of pflag that I would love to see improved in a new major release. Are you open to collaboration and discussion around |
|
@brandon1024 Contributions are always welcome! There isn't much of a collab space for this yet - mostly because I don't have enough permissions to e.g. create Github Projects, and I don't want to build on something that is tied to my own personal accounts anywhere (we already have enough issues around this library being under a user account...). Why don't we use this PR thread as a first place to discuss, and then we can move on from there when we feel there's enough maturity in the ideas to justify something more elaborate? |
|
Yep makes sense, sounds good to me 👍 Everything you described in your first comment resonates with me. Frankly, maintaining compatibility with an old, arbitrary version of the Go toolchain is a bit absurd. I stumbled into this while I was writing tests in #457. And having looked at some of these umpteen flag value types, I feel the pain.. As I'm sure you know, the flag types aren't implemented in a particularly strong way either -- raw interface types everywhere, questionable/redundant string conversions. It was quite discouraging the moment I realized I would need to patch quite a few other flag types after finishing #457. Anyway, I don't want this thread to become a rant fest. Collecting your points:
Anything else you would add here? Documentation is something I would add, particularly around how flags are parsed and any edge cases that result. Testable examples, too. And more generally, the language could use some work. Aside from the points you've mentioned, in terms of ABI or design, did you have any thoughts on the direction for v2? I have some thoughts, but I'd like to hear yours first. |
|
I started carving out this space by implementing two things: a test harness that can run the appropriate version of the upstream In addition to making sure we actually deliver on those promises, there are a bunch of promises from 1.0 I'd like to break:
When it comes to implementation details, I'd like to make use of new Go features that 1.0 couldn't due to the Go 1.12 compatibility promise:
Note: all of these thoughts are very off-the-top-of-my-head at this point. |
We should do this, but I would note that some of the POSIX guidelines cannot be easily tested here. Many of the POSIX guidelines relate to the tool itself (guidelines But I agree, we should have a suite for asserting that pflag behaves properly for well-accepted fundamental POSIX guidelines (e.g. guideline
I agree with everything here. We will need to be crystal clear on what we consider part of our compatibility promise for 2.0.0.
I've been experimenting with an implementation of GNU/POSIX arg parsing that relies on struct embedding of the standard My plan has been to prepare a Go proposal to include this as part of the standard library, but could be interesting alternative here in the spirit of the Food for thought. |
There are a lot of things in this library that I'd love to fix - but most of the fixes become a lot more complicated to execute on, because we also need to maintain backwards compatibility. (The response to release v1.0.8 shows that this is clearly important to at least some, loud, part of the community.)
I no longer want to keep adding
//nolintcomments to allow for keeping code compatible with Go 1.12, released in 2019 with the last minor release published in 2020. I no longer want to fight to keep APIs and behavior consistent across umpteen almost but not quite identical implementations of parsing flag values to slices of various primitive types. I no longer want to keep names likeBlacklistin the public API.I'm opening this PR in very early draft to start carving a path to a new major version of this library, that allows us to fix all of the above without breaking semver promises. Before this is actually released, it is likely also a good idea to move it to an import path that is not tied to a single Github user's account - but we have to start somewhere. As a first step, I just want to have CI in place to start iterating.