-
-
Notifications
You must be signed in to change notification settings - Fork 30.7k
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
Add shutil.open #47427
Comments
Currently, os.startfile is implemented in Win32 only, but there are As a result of http://portland.freedesktop.org, unix desktop has command And MacOSX has 'open' |
Those commands can be easily used through the subprocess module. |
I implemented os.startfile on posix and MacOSX as you said. but it need more work to handle error. |
Closed bpo-12522 as a duplicate. It contains a link to a discussion on python-ideas requesting the feature. |
I’m not sure we want to copy the Windows startfile call for other OSes. The os module is designed to wrap system-level calls, masking OS differences (for sendfile, for example) but not going further; it’s up to other modules (like shutil) to build more convenient APIs on top of what os provides. xdg-open is a program that’s used to open files or URIs, but it does not provide other actions like Windows’ startfile does (edit, print, etc.), nor is it backed by a system call. For these reasons, I think it’s inappropriate to implement os.startfile for non-Windows systems. People can use subprocess to run open or xdg-open. |
+1 on what Eric just said. |
So, unless someone wants to turn this request into “Add shutil.open”, I’ll close it in a few days. (I haven’t found the original discussion; if someone could add a link to the archives on mail.python.org or copy relevant quotes, it could help.) |
Eric, I have no problem with this function being placed in So I have no problem with renaming this bug to "Add shutil.open". |
This new function would call os.startfile on Windows, xdg-open where applicable, and open on Mac OS X (using a subprocess for these last two). I found the python-ideas thread: http://mail.python.org/pipermail/python-ideas/2011-July/010674.html |
Why did you add this dependency? For example, subprocess is able to locate a program if the program has no full path. We don't need a which function. |
See also lengthy discussion on bpo-1301512. |
While the Windows API call is part of Windows and the open program always present on Mac OS X, xdg-open is an optional program on free OSes, so I thought we’d need shutil.which to look before we leap. The alternative is to call Popen(['xdg-open', etc.]) and check if we get ENOENT, but I don’t know if this would be non-ambiguous (for example, do we get ENOENT if xdg-open exists but not the file?). A related question: what to do when we’re not on Windows nor Mac and xdg-open doesn’t exist? Raise NotImplemented? |
It's unambiguous. Python itself never opens the target file, it just passes the filepath string along to the xdg-open command. If Popen raises EnvironmentError, then xdg-open could not be executed. If the target file is nonexistent, then xdg-open will exit with status 2 (see aforelinked manpage). Entirely different error mechanisms.
Seems reasonable to me. So, the failure cases are: OS X and xdg-open report (2) and (4), does Windows? |
It's better to not define the function if the platform doesn't support |
You are right, I was confusing the layers! Good then.
Actually the exception is NotImplementedError. Its doc says that it’s to be used in a method that is intended to be overriden in subclasses, but I think it’s not wrong to
I think that instead of mapping error codes to custom exceptions, which is fragile and not trivial to maintain, we should just catch stderr and raise something like OSError(stderr). [Victor]
That’s easy to do if we can say detect the availability of a function in the libc, but here the function would depend on a program which could get removed or added between two calls to the function, so we have no choice. |
It runs counter to the goal of a cross-platform API if *all* the errors are platform-specific. I grant that a generic API cannot wrap *every* possible error, but (2) and (4) are amongst the most common+obvious failure modes and are (FWICT) explicitly reported by all 3 native interfaces. If we don't consolidate them, we'll basically be condemning non-trivial users to copying (or writing themselves, probably inferiorly) a chunk of boilerplate recipe code, and if we're fine with that, then why bother having (this in) the std lib at all? I don't think the handling code for them would be particularly fragile/onerous; we're talking a mere ~2 constants per platform, all tied to pretty-unlikely-to-change native interfaces. For context, here would be (AFAICT; I only have a Mac ATM) the constants in question: Windows: function return value: xdg-open (*nix): exit code: Mac OS X: exit code 1 w/ stderr output: |
@eric.araujo, @giampaolo.rodola, I'm not sure I understand why this was moved to shutil.open. It seems appropriate to try to accomplish what os.startfile() does in a cross-platform way. Don't many of the other os.* calls do this--check os.name and then "do the right thing". This is the first os.* call I've found that doesn't work on linux (though I'm sure there are many others). Is the reason because the name 'startfile' is a Windows creation and sounds Windowsy? If so, shouldn't os.startfile() be renamed to something generic like os.launch() or .launchfile()? But then you'd have reverse-compatibility issues. Why not just enhance os.startfile() so it doesn't barf if you try to use it on posix/mac? I'll try to contribute to the shutil.open() effort too. |
Here's a quick stab at 2/3rds of an implementation, and some docs. |
Test passes on Ubuntu Oneiric Linux and 'open' action appropriately defaults to launching an editor for my particular OS settings. Tests on Windows in work. |
Implement shutil.launch() & fix minor shutil.disk_usage() doctext typo Name changed from shutil.open to shutil.launch due to namespace conflict with open() builtin within the shutil module and for users that do from shutil import * On Ubuntu Oneiric Linux shutil.launch() doctest passes and Windows tests in work. Need Mac testing too. |
Nope, that refers to retro Mac OS 9 (and probably lower). Mac OS X is 'posix' for os.name purposes. |
Hobs, can you explain Also, does startfile() raise exceptions for either of the basic error conditions ("no such file" and "no associated application")? If not, I believe using the lower-level ShellExecute (http://msdn.microsoft.com/en-us/library/windows/desktop/bb762153%28v=vs.85%29.aspx ) or similar Windows API function would allow us to report such errors, as the other platform implementations currently do. |
Is the error raising PEP-3151 compliant? |
No, it isn't. Changing the The other half, the |
Why not ValueError? |
Thanks for relaunching this!
They don’t. The os module is a thin wrapper on top of system functions. Cross-platform compat is not achieved with os.name checks but with platform-specific code in the C files. As Windows provides a call named startfile, it is exposed. When we want to provide a higher-level API on top of system calls, we use other modules such as shutil.
Would you report this on another bug report or simply with a mail to the docs@python.org mailing list? Thanks.
I don’t think these are good arguments. A lot of modules expose a function named open: tarfile, codecs, tokenize... Python has namespaces, let’s use them. The argument about import * is not strong either in my opinion, because all our docs recommend against this idiom. One argument against open is that the other open functions I mention above return a file object, like the builtin open. With this in mind I agree that a better name should be found. I dislike launch though because it brings to my mind the idea of running/executing a program, not opening it in the appropriate program. |
Also: The FileNotFoundErrors quote the path twice:
Python 2.7.1 (r271:86832, Jul 31 2011, 19:30:53)
>>> path = "/foo/bar"
>>> print "Path '%s' may not exist" % repr(path)
Path ''/foo/bar'' may not exist The ValueError error message isn't grammatically correct and doesn't account for the possibility that the path is a directory (consider the case of a Unix system where the GUI file manager has been uninstalled; directories would then fail to open). May I suggest my original message?: "No application is associated with files/directories of the given type" |
The xdg-open source code (http://cgit.freedesktop.org/xdg/xdg-utils/tree/scripts/xdg-open ) shows that exit code 4 is used whenever an invocation of another opener script (e.g. kde-open, gnome-open) fails for any reason besides said script not being installed. So I'm not so sure we can jump to the conclusion that 4 automatically means FileNotFound. |
Yea, I hosed up the path quoting in a misguided attempt at shortening for On Tue, Apr 24, 2012 at 2:09 AM, Chris Rebert <report@bugs.python.org>wrote:
|
It's a universal problem on all 3 platforms. Given a script file argument, a generic "open" (as opposed to "edit") procedure will either run the script or open it in an editor, depending entirely upon the user's system configuration. Same thing happens when double-clicking a script in the file manager, which is IMO what we're trying to emulate here. It sounds like some people want a generic "(text) edit" procedure, which IMO is different enough to warrant a separate bug since there are different/more design issues to tackle (e.g. if someone edit()s an image file (or a file of uncertain type) on Unix, what application is opened, and how is that determined?). And no peeking at Mercurial's code; it's under GPLv2, whereas Python is under BSD/MIT-like licensing, making them incompatible. |
I'm not a lawyer (duh), but my understanding is that *looking* at GPL code (as opposed to proprietary code, where someone might sue you about it and not looking is a good defense) is OK, you just can't copy it. |
You could probably copy a one- or two-liner, especially if it's not a |
I can see why this partial implementation of I'll delete the OS9 ('mac') test comment and leave an implementation for
I'll test all the windows and linux exception possabilities and get back to Cheers,
|
Just to note there's http://pypi.python.org/pypi/desktop/0.4 out there. |
Had no idea. Sounds like a good place for it.
|
Well, so how about on UNIX shutil.launch (or whatever it's called) first checks to see if we're referring to a file. If we are, check to see if it's marked executable. If it is, execute it under a shell. Failing *that* we could run xdg-open where available. |
Could even add an
'share', 'send', 'like', 'email', 'open', 'xdg-open', ...] # can Each op requires 1 conditional and gives a lot more utility without On Fri, May 25, 2012 at 4:40 AM, Larry Hastings <report@bugs.python.org>wrote:
|
IIRC ShellExecute on Windows has support for verbs like this. But how would we implement support for "explore" / "share" / "send" / "like" on Mac OS X and Linux? The only flag I can think of supporting in a cross-platform way would be "execute=True", which on Windows would mean try the verb "run" before trying the default, and on OS X and Linux would mean look for the execute bit / the "#!" signature and run it if possible first before using "xdg-open". |
In Linux we could That way the API remains fixed and implementation can mature without --Hobson
|
What if they're using a KDE-based system? Or XFCE? Or xmonad? Or qtile? Designing a cross-platform API based on abilities provided by only one of those platforms, then waiving your hand and saying "I'm sure the other platforms will add this functionality sometime", is poor cross-platform API design. OS X is 11 years old, GNOME is 13, KDE is 16, and none of the above appear to be in a hurry to add this feature. And even on Windows, which has had this feature for 17 years (IIRC it was introduced with Windows 95)... it is hardly ever used. 99.999% of the time you simply want to "open" the file using the user's preferred app. Since Windows users already have an API to access this extra functionality on their platform (os.startfile) I assert that the cross-platform API should only expose the functionality that's available across platforms. Hence my proposal for "execute". |
Then they or we can add Konqueror, etc to the options that are 'try'ed for Gnome/KDE Xdg-open, Android "Share", Windows Open, already do implement But clearly you have passion for the boolean execute flag, and you probably On Sat, May 26, 2012 at 4:00 PM, Larry Hastings <report@bugs.python.org>wrote:
|
The latest discussion on this thread does not match my UNIX user model. tl;dr: shutil.whatever should call startfile(..., 'edit') or xdg-open; other actions implemented by startfile are not cross-platform.
This makes sense from an os.startfile perspective, but not for an xdg-open user. xdg-open (and shutil.whatever in my eyes) only starts an application with the passed argument; that’s it. There is no execute action because that’s done otherwise (in Python, with subprocess and os.exec*; in the shell, with program invocation (i.e. “program” or “/full/path/to/file”) using shebangs or binfmt-format). So in my mental model, using xdg-open to have a file or URI opened by a configured application (which can be graphical or command-line) has nothing to do with executing a file. (There was an older system on UNIX named mailcap where you could register console and GUI actions for different actions, like edit and print, but it’s not used by current graphical mail clients like Thunderbird or by the xdg-open system, so I would not consider it at all.) |
Note that my suggestion has an inconvenient on Windows. Take the example of HTML files; on an UNIX system I would decide whether I want them to be opened with my text editor or my web browser; I’d choose web browser because I launch my text editor from a shell. On Windows though you would register your web browser for default action and your text editor for the edit action; this deliberate configuration would not be respected if we implement shutil.whatever with os.startfile(..., 'edit'). |
(A last, very minor thing: please trim or remove quoted text when replying with the email interface (or on mailing lists); it puts unneeded wall of texts that reader waste time scanning, in case there were inline replies. Thanks in advance.) |
shutil.whatever with os.startfile(..., 'edit'). That's why I thought an 'auto' option might be useful--to allow the caller |
MacOSX provides functionality simular to os.startfile in the LaunchServices framework. I've tried to prototype a startfile implementation using this framework, (see bpo-3177-os_startfile_macosx.txt) but I'm not too happy about it because the APIs don't actually work as expected. In particular:
Other notes:
|
xdg-open is just a shell script that calls desktop-specific launchers. The behavior of these launchers is very different when it comes to executables. There is no guarantee that calling xdg-open for a script will open it for editing. For example, there are 3 launchers on my XFCE machine: exo-open (native for XFCE), gvfs-open (GNOME), and kde-open. When called with a .py script with shebang line and executable bit set, exo-open and kde-open open text editor, but gvfs-open launches the script. When called for a real exe, exo-open and gvfs-open launch it, but kde-open brings a message box saying that the exe will not be started for safety reasons. The patch raises NoAssociatedApplicationError when xdg-open returns 3, but this error code actually means that a launcher was not found. |
same problem here |
1 similar comment
This comment was marked as duplicate.
This comment was marked as duplicate.
Hello from 2024! 👋 Any hope of getting a shutil.open implementation landed still? It looks like this issue stalled out after 2012... It's been discussed on a popular StackOverflow question, and there's now a universal-startfile package on PyPI attempting to offer the same thing. |
I don’t think there is a well thought-out proposal, or a big need for this. |
What's the rationale for "not a big need"? To me the popular StackOverflow questions (+ lots of dupes of it) suggest otherwise. I was actually considering to publish my own third-party module for it, but would prefer to make it a drop-in polyfill with whatever's planned for standard library here (and the half-working os.startfile) instead of trying to freehand a signature myself. @ALL could anyone help direct me to min viable behavior if I were volunteering to implement it in a module? |
K, I've implemented a quick https://pypi.org/project/shopen/ with my understanding of what a shutil.open should offer. IMO it'd still be great to have those batteries come included with python, but for now I can iterate there on ironing out the wrinkles. |
Often |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: