I’ve recently started work on a cross-platform approach to sending (and retrieving) files from the system trash. Linux support is roughly complete, and I’ll be tackling MacOS next. This could either take the form of a feature update to BaseDirs.jl, or I could make a new packages say Trash.jl for this.
Bundle with BaseDirs.jl
Make a new Trash.jl package
0voters
I’m opening this thread to let people know this is happening, and give an opportunity for early feedback/comment/questions
Indeed, there’s a bit too it and I’m going to need to pull in Dates (and BaseDirs if done as a separate package) as a dep for this (BaseDirs currently only depends on Base).
Thankfully the logic is well-described in the Freedesktop Trash spec, and I know the author of trash-d which has been helpful.
The most annoying part so far has been getting a pure-julia replacement for du -B1 which has culminated in finding a statdoc innacuracy.
MacOS is going to be much more of a pain than it was with locating directories, as it looks like I’ll need to hook into the Objective-C runtime that apple ships.
Windows seems like it will be a similar deal to earlier, calling the Win32 API with an expected amount of suffering and “why microsoft?” involved in the development.
BaseDirs.jl currently has a clear and clean scope, to provide information about (and optionally create) the specific base directories mentioned. Functionality to send/retrieve files in the system trash, while related, feels like it distorts that scope and makes it less clean.
On the other hand, a new package just for this seems excessive (I assume that contributes to the decision of people who have voted against it). Instead - at the risk of even more cross-platform finagling work - perhaps there’s a more general package you could create for such operations, including things like:
creating and managing desktop shortcuts
hiding/unhiding files
file associations (default “Open with” programs)
etc.
Of course, none of these need to be present initially in the package, it can just start off with the system trash handling features. These could just be things to help decide on a package name.
FilesystemExtras.jl was the first name that popped up in my head, but these aren’t strictly filesystem operations afaict, they live on the borderlands of filesystem/OS/desktop manager. Since people often do these operations via their system file manager, perhaps FileManager.jl? (Sounds a bit too general without that context though.)
Mmm, something weighing on my mind is that I really want BaseDirs to be small enough that it can be added on a whim to packages without worry — and hopefully before people do something worse like hardcode a particular OS’s approach or shoving everything under the julia depot.
I think it currently does that rather well, with 1.10 I see:
julia> @time using BaseDirs
0.000083 seconds (208 allocations: 19.539 KiB)
Testing the current trash code, it “blows up” using BaseDirs to ~0.02 seconds and ~20k allocations. While this is far from large, it is markedly less lightweight.
This does push me more towards making Trash.jl. At the same time, I’m wary of scattering such functionality too thin.
TBH I half feel that it would make sense for system base directory and trashing functionality to come with Base.Filesystems, but this is probably a controversial view.
It occurs to me that perhaps a best of both worlds approach could be to both create this menagerie of small packages, and then perhaps a meta-package called something like OsIntegrations that simply loads them all under a single API.
I’d be keen to hear more thoughts on this (and the wider topic of how system integration should be approached by Julia. It’s nice to have our own managed .julia but I think there’s a point where it makes sense to be a “good citizen” on the OS and recognise that the computer isn’t just a Julia machine[1] ).
That sounds like the completely right solution to me. It allows us to load the meta-package for general usage, while allowing developers to only add the required parts as dependencies. All without duplicating the work. Perhaps a “Filesystem” github organization is in order? I am not sure if there are enough package for an org to make sense.
It may be reasonable, Trash.jl has also been argued as reasonable, and I find it more so. I would never have thought to look for XDG or BaseDirs.jl related to trash. Even as a Linux users, I don’t think about those. XDG is not for e.g. Windows, I see at BaseDirs.jl:
It is essentially an implementation of the XDG (Cross-Desktop Group) directory specifications, with analogues for Windows and MacOS for cross-platform. More specifically, this is a hybrid of:
The XDG base directory and the XDG user directory specifications on Linux
The Known Folder API on Windows
The Standard Directories guidelines on macOS
I’m first now hearing of “Standard Directories guidelines”, that may or may not have to do with trash.
On Windows, I get a `HWND` not defined in `Trash` error during precompilation. Seems like that one got missed out among the const definitions.
Full error
Precompiling project…
✗ Trash
0 dependencies successfully precompiled in 36 seconds. 214 already precompiled.
1 dependency errored.
For a report of the errors see julia> err. To retry use pkg> precompile
julia> err
PkgPrecompileError: The following 1 direct dependency failed to precompile:
Trash
Failed to precompile Trash [2177afbf-83db-417c-8225-ea8191cd3f0f] to “C:\Users\Sundar\.julia\compiled\v1.11\Trash\jl_DC03.tmp”.
ERROR: LoadError: UndefVarError: HWND not defined in Trash
Stacktrace:
[1] top-level scope
@ :0
[2] top-level scope
@ C:\Users\Sundar.julia\packages\Trash\ojl4l\src\windows.jl:475
[3] include(mod::Module, _path::String)
@ Base .\Base.jl:557
[4] include(x::String)
@ Trash C:\Users\Sundar.julia\packages\Trash\ojl4l\src\Trash.jl:4
[5] top-level scope
@ C:\Users\Sundar.julia\packages\Trash\ojl4l\src\Trash.jl:23
[6] include
@ .\Base.jl:557 [inlined]
[7] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String},
concrete_deps::Vector{Pair{Base.PkgId, UInt128}}, source::Nothing)
@ Base .\loading.jl:2881
[8] top-level scope
@ stdin:6
in expression starting at C:\Users\Sundar.julia\packages\Trash\ojl4l\src\windows.jl:475
in expression starting at C:\Users\Sundar.julia\packages\Trash\ojl4l\src\Trash.jl:4
in expression starting at stdin:
list() always returns only files that have been trashed from the C: drive, whereas the system “Recycle Bin” lists all files from all drives. Doing a Trash.list(Trash.trashdir("G:/")) does return trashed files from that drive. But changing Julia’s current dir with cd("G:\\") seems to have no impact, list() still returns C:'s trashed files only. Not necessarily an issue, but at least worth documenting.
untrash’s docs do mention path (and not filename) repeatedly for the first arg, so this is mostly my bad, but it would be nice if the error messages from untrash showed the absolute path it actually looks for, instead of just the argument as is. I was giving it the filename as I saw it in the Recycle Bin, and getting confused why it was saying it’s “not present in the trash” (seems like bare filenames are treated as relative paths relative to current dir).
Looking at the code, untrash carefully selects the entry as per the user’s pick and then just seems to ignore it - I’d guess the first(candidates) in the last line is a remnant of old code, and it’s supposed to be entry there?
Yes, currently list() defaults to telling you about the trash that your homedir would use. I’m not sure if this is better/worse than the current directory. Regardless, I agree the docs could benefit from clarification.
Oh yes? That’s interesting. I imagine there’s probably an API for listing all drives… Hmm, I’m not sure what a good cross-platform-generalisable behaviour here would be.