-
Notifications
You must be signed in to change notification settings - Fork 17.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
io/fs: add file system interfaces #41190
Comments
Accepting this proposal would also let us land the embedded files draft design in Go 1.16, which I've proposed in #41191. |
This is my concern. It's not that the alternative (attempting to define all FS methods in one huge interface which packages may then implement as no-op) is better. Rather, my perception is that the ergonomics of this approach are poor enough that it won't achieve broad community adoption and the level of composability and general success that interfaces like For example, it's clear that I will be able to pipe a zip file to In spite of my concerns, it seems like the best approach available to Go at this time, and I anticipate it will be accepted given that the very exciting #41191 depends upon it. However, I have this inkling that the advent of generics may allow a more powerful/robust/safe abstraction. Has any thought been given to this, or to how |
The feedback page: I think this API looks promising... and would benefit from a prototype phase. A lot of feedback was posted, but there's been rather light discussion of the comments, presumably because you can't subscribe to a Reddit thread, and/or many in Go's github-centered community don't frequent Reddit. It would help to see a review and analysis of feedback here, and perhaps a roadmap to likely future features. Problems were identified with the Landing a prototype in x/ seems like a logical step before stdlib. Go has long been deliberative and conservative about new features. Is this urgent somehow? FWIW, my Go apps make heavy use of the filesystem, on Windows, MacOS, and Linux. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Discussion of ErrNotImplemented has moved to #41198. |
This comment has been minimized.
This comment has been minimized.
@networkimprov, see https://go.googlesource.com/proposal/+/master/design/draft-iofs.md#why-not-in-golang_org_x. |
I'd feel more comfortable with this if it included basic combinatory file systems either in io/fs or somewhere official like golang.org/x. They would still have the issue with not understanding nonstandard optional methods but they would at least be guaranteed to keep up with official optional methods. The two file systems I'm thinking of are an overlay fs and one that can "mount" other file systems in subdirectories of its root. With those two you could stitch multiple fs together easily. |
@jimmyfrasche I don't understand the difference between "an overlay fs" and "one that can mount other file systems in subdirectories of its root." I agree we should provide something like that, and we intend to. But those sound like the same thing to me. :-) |
I was thinking just:
interface {
FS
Mount(dirName string, fs FS) error
} and not have any files other than those mounted. |
Got it, thanks @jimmyfrasche: union vs replace. |
The io/fs integration with stdlib, and embed.Files can all be prototyped in x/ I wasn't suggesting x/ as the permanent home. EDIT: Also, Readdir() & FileInfo have performance problems and missing features. The replacement APIs need prototypes. A draft is in #41188 (comment) |
I have two comments. I found similar comments in the reddit thread, but didn't see a satisfying discussion/conclusion. Apologies if I missed previous conclusions. WrappingI think we should consider an official mechanism to wrap fs.FS objects (and probably fs.File objects). For example, I want to wrap an fs.FS to track the total number of bytes read. I need to intercept calls to fsys.Open and calls to fsys.ReadFile, if implemented. I also don't want to lose any other optional interfaces such as fs.GlobFS. Based on my experience with http.ResponseWriter, this is commonly needed, but hard and tedious to do correctly For a concrete idea to discuss, something like this: type FSWrapper struct {
FS
OpenFunc func(name string) (File, error)
ReadFileFunc func(name string) ([]byte, error)
// ... all other extensions ...
}
func (w *FSWrapper) ReadFile(name string) ([]byte, error) [
rf, ok := w.FS.(ReadFileFS)
if !ok {
return nil, errors.ErrNotImplemented
}
if w.ReadFileFunc != nil {
return w.ReadFileFunc(name)
} else {
return rf.ReadFileFunc(name)
}
} Granted there are cases where a generic wrapper would expose extensions you don't want to pass through. Anyway, I think at least the proposal would benefit from discussion or FAQ addressing wrapping. WritingIt seems like we are starting with read-only because that is the simplest interface that enables the motivating embed feature. However, in some sense writing is more fundamental because you have to write before you can read (only half joking). I worry writing will be relegated to second class citizenship forever due to optional interfaces. For example, the hypothetical OpenFile extension: func OpenFile(fsys FS, name string, flag int, perm os.FileMode) (File, error) OpenFile returns an fs.File which has no Write method. It seems a bit strange to always have to type assert to get a writable file. I think the eternal friction between io.Writer and fs.File as proposed will be more painful than starting with a broader proposal. In particular, I think we should consider:
|
Guys, PLEASE just add context everywhere! It costs nothing to ignore it or add context.TODO() for callers, but it will make life of network filesystem implementers and users much easier. In particular, it's needed for better contextual logging and cancellation. You're all strictly opposed to thread-local variables, but then why are you designing APIs without a way to pass a context?!? |
Deadlines and interrupts have been suggested as another way to solve the same problem, without affecting every function signature. It's unlikely that the os package will add dozens of new APIs with |
I think the simplest way to solve this is to pass a context on filesystem creation. So instead of having a type implementing |
Cancelling all pending ops by the stdlib file API (which will implement fs.FS) is not desirable. It probably isn't useful for other FS types, as well. The common case, in my experience, is interrupting any pending ops trying paths within a tree rooted at a certain path. An API for that looks like one of:
Note that os.File already supports Read & Write deadlines. I doubt that |
Gah. The deadlines/interrupts design is just horrible. No, it's seriously horrible. The whole idea for not including thread IDs in Golang was to make sure APIs are forced to deal with clients potentially running in multiple goroutines. Introducing the per-FS state will defeat this purpose, making the FS object behave more like a TCP connection rather than a dispatcher for an underlying FS. And only one goroutine at a time would be able to use it, otherwise they might step on each others' toes with deadlines. Never mind the badness of introducing a hidden state where it arguably shouldn't even be in the first place. What are the actual downsides of simply adding context.Context to every method? |
This will require the FS implementation to be a thin wrapper that supplies context to the underlying implementation. Certainly doable, but still ugly. And it will still introduce dependency on context.Context in the FS code. |
@Cyberax All you need is This has been before with |
Change https://golang.org/cl/273946 mentions this issue: |
Do you have plan to provide way to update ModTime in embed file? Current implementation of ModTime return always zero-time. So http.FS does not set Last-Modified header. i.e. browser does not cache the contents. I know it's possible to implement with adding ETag but not easy way, I think. |
IIRC ETag is already implemented in http.FS |
FS support ETag but it seems only scan. FS does not set ETag. I wrote small example code. But server does not set ETag header. |
Ah, u are right. |
I wanted to follow up on the mounting/overlay discussion a bit. I was experimenting with a package this weekend for self-hosting a copy of Swagger UI with All that said, this use-case is fairly specific, but I can see it being useful functionality elsewhere, and I was wondering if there were any plans to allow for layering filesystems or something like I can see the implementation for the latter being relatively complex, but, at least for this case I could easily work around it with mount-like functionality, and Edit: having gotten most of the way through a prototype implementation of an |
Change https://golang.org/cl/285093 mentions this issue: |
For #40700 For #41190 Change-Id: I964d6856d5cad62c859d0f3a7afdd349a8ad87cb Reviewed-on: https://go-review.googlesource.com/c/go/+/285093 Trust: Ian Lance Taylor <iant@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Change https://golang.org/cl/285592 mentions this issue: |
Change https://golang.org/cl/285593 mentions this issue: |
Change https://golang.org/cl/285673 mentions this issue: |
For #38781 For #40700 For #41190 Change-Id: I72f1055e51edb517041d3861640734ba6ef5f342 Reviewed-on: https://go-review.googlesource.com/c/go/+/285673 Trust: Ian Lance Taylor <iant@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
For #40700 For #41467 For #41190 Change-Id: Id94e7511c98c38a22b1f9a55af6e200c9df07fd3 Reviewed-on: https://go-review.googlesource.com/c/go/+/285592 Trust: Ian Lance Taylor <iant@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
For #40700 For #41190 Change-Id: I8ade6efd5be09003fc3e5db5a9b91ba6e0f023f7 Reviewed-on: https://go-review.googlesource.com/c/go/+/285593 Trust: Ian Lance Taylor <iant@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Well done everyone for the discourse, the work, and reviews on io/fs during the Go1.16 cycle. I believe we can now close this issue given all the prior work: @rsc @robpike @ianlancetaylor would y’all like to do the honors of closing it, or shall I? Thank you. |
Closing this issue as folks seem swamped, congrats everyone! |
Update references missed in CL 263142. For #41190 Change-Id: I778760a6a69bd0440fec0848bdef539c9ccb4ee1 GitHub-Last-Rev: dda42b0 GitHub-Pull-Request: #42874 Reviewed-on: https://go-review.googlesource.com/c/go/+/273946 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Trust: Cherry Zhang <cherryyz@google.com>
In July, @robpike and I posted a draft design for file system interfaces. That doc links to a video, prototype code, and a Reddit discussion.
The feedback on that design has been almost entirely positive.
A few people raised concerns about the use of optional interfaces, but those are an established pattern in Go that we understand how to use well (informed in part by some earlier mistakes, such as optional interface like http.Hijacker with methods that cannot return an error to signal failure/unavailability).
A few people suggested radical redesigns of the
os.File
interface itself, but for better or worse installed base and the weight of history cautions against such drastic changes.I propose to adopt the file system interfaces draft design for Go 1.16.
The text was updated successfully, but these errors were encountered: