Skip to content
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

Closed
ganadist mannequin opened this issue Jun 23, 2008 · 67 comments
Closed

Add shutil.open #47427

ganadist mannequin opened this issue Jun 23, 2008 · 67 comments
Labels
stdlib Python modules in the Lib dir type-feature A feature request or enhancement

Comments

@ganadist
Copy link
Mannequin

ganadist mannequin commented Jun 23, 2008

BPO 3177
Nosy @ronaldoussoren, @tebeka, @pitrou, @larryhastings, @giampaolo, @benjaminp, @mcepl, @merwok, @bitdancer, @smarnach, @Fak3, @danpla
Files
  • os_startfile.diff: patch that implemented os.startfile()
  • shutil_open.py: implementation sketch
  • shutil_launch.py: The last 1/3rd of Rebert's implementation of shutil.open()
  • shutil_open.patch: Implementation of shutil_open feature, untested on Windows
  • shutil_open.patch
  • shutil_open.patch: provides shutil.launch() to do os.startfile() cross-platform
  • shutil_open.patch: another round of improvement
  • shutil_open.patch: revised patch
  • issue3177-os_startfile_macosx.txt
  • 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:

    assignee = None
    closed_at = None
    created_at = <Date 2008-06-23.11:38:23.901>
    labels = ['type-feature', 'library']
    title = 'Add shutil.open'
    updated_at = <Date 2018-03-02.17:06:15.586>
    user = 'https://bugs.python.org/ganadist'

    bugs.python.org fields:

    activity = <Date 2018-03-02.17:06:15.586>
    actor = 'Socob'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2008-06-23.11:38:23.901>
    creator = 'ganadist'
    dependencies = []
    files = ['10715', '25310', '25311', '25312', '25315', '25317', '25319', '25332', '26624']
    hgrepos = []
    issue_num = 3177
    keywords = ['patch']
    message_count = 62.0
    messages = ['68623', '68650', '68664', '140059', '140275', '140276', '140280', '140283', '140415', '140416', '140418', '140419', '157216', '157246', '157247', '157258', '159000', '159001', '159004', '159005', '159006', '159007', '159008', '159009', '159010', '159017', '159020', '159029', '159030', '159034', '159037', '159038', '159039', '159043', '159044', '159051', '159056', '159058', '159059', '159062', '159063', '159064', '159065', '159068', '159069', '159121', '159505', '159507', '161569', '161613', '161631', '161687', '161688', '161837', '161852', '161853', '161854', '161858', '167011', '310214', '311549', '311552']
    nosy_count = 19.0
    nosy_names = ['ronaldoussoren', 'tebeka', 'pitrou', 'larry', 'giampaolo.rodola', 'benjamin.peterson', 'mcepl', 'eric.araujo', 'ganadist', 'Arfrever', 'r.david.murray', 'cvrebert', 'rosslagerwall', 'smarnach', 'Roman.Evstifeev', 'Hobson.Lane', 'plakhotich', 'Socob', 'amirjn']
    pr_nums = []
    priority = 'low'
    resolution = None
    stage = None
    status = 'open'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue3177'
    versions = ['Python 3.3']

    @ganadist
    Copy link
    Mannequin Author

    ganadist mannequin commented Jun 23, 2008

    Currently, os.startfile is implemented in Win32 only, but there are
    command line tools in Unix and MacOSX that have same behavior.

    As a result of http://portland.freedesktop.org, unix desktop has command
    line tool named "xdg-open"
    (http://portland.freedesktop.org/xdg-utils-1.0/xdg-open.html).

    And MacOSX has 'open'
    (http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/open.1.html)
    command, and it can be used in same condition.

    @ganadist ganadist mannequin added stdlib Python modules in the Lib dir type-feature A feature request or enhancement labels Jun 23, 2008
    @benjaminp
    Copy link
    Contributor

    Those commands can be easily used through the subprocess module.
    However, if a patch is provided, it could be accepted.

    @benjaminp benjaminp changed the title os.startfile implement in posix and MacOSX implement os.startfile on posix and MacOSX Jun 23, 2008
    @ganadist
    Copy link
    Mannequin Author

    ganadist mannequin commented Jun 24, 2008

    I implemented os.startfile on posix and MacOSX as you said.

    but it need more work to handle error.

    @rosslagerwall
    Copy link
    Mannequin

    rosslagerwall mannequin commented Jul 9, 2011

    Closed bpo-12522 as a duplicate.

    It contains a link to a discussion on python-ideas requesting the feature.

    @merwok
    Copy link
    Member

    merwok commented Jul 13, 2011

    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.

    @giampaolo
    Copy link
    Contributor

    +1 on what Eric just said.
    See bpo-10882 and msg 126049

    @merwok
    Copy link
    Member

    merwok commented Jul 13, 2011

    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.)

    @cool-RR
    Copy link
    Mannequin

    cool-RR mannequin commented Jul 13, 2011

    Eric, I have no problem with this function being placed in shutil instead of os, as long as it's implemented and it's in the standard library, and people don't have to use subprocess to run open or xdg-open themselves as I currently do.

    So I have no problem with renaming this bug to "Add shutil.open".

    @merwok
    Copy link
    Member

    merwok commented Jul 15, 2011

    as long as it's implemented and it's in the standard library, and
    people don't have to use subprocess to run open or xdg-open themselves
    as I currently do.

    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
    There was only limited support.

    @merwok merwok changed the title implement os.startfile on posix and MacOSX Add shutil.open Jul 15, 2011
    @vstinner
    Copy link
    Member

    dependencies: + Finding programs in PATH, adding shutil.which

    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.

    @merwok
    Copy link
    Member

    merwok commented Jul 15, 2011

    See also lengthy discussion on bpo-1301512.

    @merwok
    Copy link
    Member

    merwok commented Jul 15, 2011

    > dependencies: + Finding programs in PATH, adding shutil.which
    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.

    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?

    @cvrebert
    Copy link
    Mannequin

    cvrebert mannequin commented Mar 31, 2012

    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?).

    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.

    A related question: what to do when we’re not on Windows nor Mac and xdg-open doesn’t exist? Raise NotImplemented?

    Seems reasonable to me.

    So, the failure cases are:
    (1) Platform doesn't support this feature -> raise NotImplemented
    (2) Target file doesn't exist
    (3) Target file is inaccessible
    (4) No application is associated with the file type in question

    OS X and xdg-open report (2) and (4), does Windows?
    OS X reports (3) indirectly/vaguely [generic exit status 1 w/ possibly unstable error message]; don't know what Windows and xdg-open do here.

    @vstinner
    Copy link
    Member

    vstinner commented Apr 1, 2012

    (1) Platform doesn't support this feature -> raise NotImplemented

    It's better to not define the function if the platform doesn't support
    the feature.

    @merwok
    Copy link
    Member

    merwok commented Apr 1, 2012

    > [...] do we get ENOENT if xdg-open exists but not the file?).
    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.

    You are right, I was confusing the layers! Good then.

    So, the failure cases are:
    (1) Platform doesn't support this feature -> raise NotImplemented

    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

    (2) Target file doesn't exist
    (3) Target file is inaccessible
    (4) No application is associated with the file type in question

    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]

    It's better to not define the function if the platform doesn't support the feature.

    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.

    @cvrebert
    Copy link
    Mannequin

    cvrebert mannequin commented Apr 1, 2012

    > (2) Target file doesn't exist
    > (4) No application is associated with the file type in question
    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)

    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:
    ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND
    SE_ERR_NOASSOC

    xdg-open (*nix): exit code:
    2
    3

    Mac OS X: exit code 1 w/ stderr output:
    "The file XXX does not exist."
    "No application knows how to open XXX."

    @HobsonLane
    Copy link
    Mannequin

    HobsonLane mannequin commented Apr 23, 2012

    @eric.araujo, @giampaolo.rodola,
    (http://bugs.python.org/issue3177#msg140275)

    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.

    @cvrebert
    Copy link
    Mannequin

    cvrebert mannequin commented Apr 23, 2012

    Here's a quick stab at 2/3rds of an implementation, and some docs.

    @HobsonLane
    Copy link
    Mannequin

    HobsonLane mannequin commented Apr 23, 2012

    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.

    @HobsonLane
    Copy link
    Mannequin

    HobsonLane mannequin commented Apr 23, 2012

    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 ./python -m test -j3 doesn't change (OS-appropriate tests all pass).

    Windows tests in work. Need Mac testing too.

    @cvrebert
    Copy link
    Mannequin

    cvrebert mannequin commented Apr 23, 2012

    or os.name == 'mac' ???

    Nope, that refers to retro Mac OS 9 (and probably lower). Mac OS X is 'posix' for os.name purposes.

    @cvrebert
    Copy link
    Mannequin

    cvrebert mannequin commented Apr 23, 2012

    operation seems questionable. IMO, the verbs seem stronger / more important than mere optional suggestions (particularly "open" vs. "edit" for files with read-only viewers), and only Windows supports them (so anyone requiring that feature might as well just use startfile() directly). By virtue of this function being cross-platform, we're kinda limited to just supporting the lowest common denominator.

    Hobs, can you explain gui?

    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.

    @bitdancer
    Copy link
    Member

    Is the error raising PEP-3151 compliant?

    @cvrebert
    Copy link
    Mannequin

    cvrebert mannequin commented Apr 23, 2012

    No, it isn't. Changing the IOError(errno.ENOENT, "...s to FileNotFoundError("...s would half fix it.

    The other half, the OSError(errno.ENOSYS)s, has a FIXME for what's the right error to raise in that case ("no application associated with files of this type"). I have no idea myself. None of the new PEP-3151 errors apply. Nor did any of the errnos strictly speaking AFAICT; ENOSYS was the closest approximation I could find. Thoughts? Custom error class? Different errno? Something else?

    @pitrou
    Copy link
    Member

    pitrou commented Apr 23, 2012

    ENOSYS was the closest approximation I could find. Thoughts? Custom
    error class? Different errno? Something else?

    Why not ValueError?

    @merwok
    Copy link
    Member

    merwok commented Apr 23, 2012

    Thanks for relaunching this!

    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".

    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.

    fix minor shutil.disk_usage() doctext typo

    Would you report this on another bug report or simply with a mail to the docs@python.org mailing list? Thanks.

    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 *

    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.

    @cvrebert
    Copy link
    Mannequin

    cvrebert mannequin commented Apr 23, 2012

    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"

    @cvrebert
    Copy link
    Mannequin

    cvrebert mannequin commented Apr 23, 2012

    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.

    @HobsonLane
    Copy link
    Mannequin

    HobsonLane mannequin commented Apr 23, 2012

    Yea, I hosed up the path quoting in a misguided attempt at shortening for
    80-col line-wrapping. Yours is better, will revert.

    On Tue, Apr 24, 2012 at 2:09 AM, Chris Rebert <report@bugs.python.org>wrote:

    Chris Rebert <pybugs@rebertia.com> added the comment:

    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"

    ----------


    Python tracker <report@bugs.python.org>
    <http://bugs.python.org/issue3177\>


    @cvrebert
    Copy link
    Mannequin

    cvrebert mannequin commented Apr 23, 2012

    The semantics of "associated application" change considerably from
    operating system to operating system. As an example,
    os.startfile("a.py") will usually run a.py in the Python
    interpreter, while xdg-open a.py it will usually open the source
    code in an editor on Linux.

    Outch. I think the behavior should be more similar than that, i.e. that the function should use startfile with the edit action on Windows.

    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.

    @bitdancer
    Copy link
    Member

    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.

    @pitrou
    Copy link
    Member

    pitrou commented Apr 23, 2012

    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
    very creative one.
    By that I mean that putting "i = i + 1" under the GPL is not enough to
    prevent anyone else to write the same line of code :-)

    @HobsonLane
    Copy link
    Mannequin

    HobsonLane mannequin commented Apr 24, 2012

    I can see why this partial implementation of operation in this ver seems
    useless. But it is a placeholder for eventually providing Linux/Mac users
    with the same functionality as windows. The os.startfile() or
    shutil.launch() function can easily fill the gap left by the OS, which is
    what os does for lots of other missing OS features on one platform or
    another.

    I'll delete the OS9 ('mac') test comment and leave an implementation for
    those platforms up to others?

    gui is for software that intends to launch user's $EDITOR, vi, nano,
    emacs, etc. This is intended to generalize startfile to encompass a common
    pattern, e.g. in bzr, hg. Perhaps it doesn't belong in ver1 until we sort
    out all the other uncomfortable things about this patch.

    I'll test all the windows and linux exception possabilities and get back to
    you on what exceptions startfile() normally raises, and whether this
    implementation of shutil.launch() raises comparable exceptions.

    Cheers,
    H
    On Apr 23, 2012 7:53 PM, "Chris Rebert" <report@bugs.python.org> wrote:

    Chris Rebert pybugs@rebertia.com added the comment:

    operation seems questionable. IMO, the verbs seem stronger / more
    important than mere optional suggestions (particularly "open" vs. "edit"
    for files with read-only viewers), and only Windows supports them (so
    anyone requiring that feature might as well just use startfile() directly).
    By virtue of this function being cross-platform, we're kinda limited to
    just supporting the lowest common denominator.

    Hobs, can you explain gui?

    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.

    ----------


    Python tracker <report@bugs.python.org>
    <http://bugs.python.org/issue3177\>


    @tebeka
    Copy link
    Mannequin

    tebeka mannequin commented Apr 27, 2012

    Just to note there's http://pypi.python.org/pypi/desktop/0.4 out there.

    @HobsonLane
    Copy link
    Mannequin

    HobsonLane mannequin commented Apr 28, 2012

    Had no idea. Sounds like a good place for it.
    On Apr 28, 2012 6:54 AM, "Miki Tebeka" <report@bugs.python.org> wrote:

    Miki Tebeka <miki.tebeka@gmail.com> added the comment:

    Just to note there's http://pypi.python.org/pypi/desktop/0.4 out there.

    ----------
    nosy: +tebeka


    Python tracker <report@bugs.python.org>
    <http://bugs.python.org/issue3177\>


    @larryhastings
    Copy link
    Contributor

    As an example, os.startfile("a.py") will usually run a.py
    in the Python interpreter, while xdg-open a.py it will
    usually open the source code in an editor on Linux.

    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.

    @HobsonLane
    Copy link
    Mannequin

    HobsonLane mannequin commented May 25, 2012

    Could even add an operation parameter to let the caller select actions,
    including 'auto' implemented as Larry suggests. Sometimes you feel like
    trusting the user's xdg-open preferences/settings. Sometimes you don't.
    Easy enough to let the caller choose, rather than the OS.

    operation in ['auto', 'run', 'edit', 'display', 'browse', 'explore',
    

    'share', 'send', 'like', 'email', 'open', 'xdg-open', ...] # can
    be incrementally added/implemented

    Each op requires 1 conditional and gives a lot more utility without
    requiring much more launch/action code that hasn't already been
    tested/debugged on all relevant platforms. And the operation parameter is
    a semi-standard used by MS, easing the transition for Win-devs migrating
    gui code to python and linux (or cross-platform implementations).

    On Fri, May 25, 2012 at 4:40 AM, Larry Hastings <report@bugs.python.org>wrote:

    Larry Hastings larry@hastings.org added the comment:

    As an example, os.startfile("a.py") will usually run a.py
    in the Python interpreter, while xdg-open a.py it will
    usually open the source code in an editor on Linux.

    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.

    ----------
    nosy: +larry


    Python tracker <report@bugs.python.org>
    <http://bugs.python.org/issue3177\>


    @larryhastings
    Copy link
    Contributor

    Could even add an operation parameter to let the caller
    select actions,
    [...]
    operation in ['auto', 'run', 'edit', 'display', 'browse',
    'explore', 'share', 'send', 'like', 'email', 'open', 'xdg-open',
    ...] # can be incrementally added/implemented

    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".

    @HobsonLane
    Copy link
    Mannequin

    HobsonLane mannequin commented May 26, 2012

    In Linux we could try nautilus then Mozilla
    (file://.../containing_folder) then fall back to a shell cd && ls if no
    browser is available, raising NotImplemented if all else fails... until
    someone implements for that user's platform particulars. Likewise on OSX,
    Mac, even iOS, Android, etc.

    That way the API remains fixed and implementation can mature without
    breaking it, as people think of or develop new cross-platform actions that
    you and I can't even dream of now. A boolean execute flag is
    unnecessarily specialized (not generalized or flexible).

    --Hobson
    On May 25, 2012 5:08 PM, "Larry Hastings" <report@bugs.python.org> wrote:

    Larry Hastings larry@hastings.org added the comment:

    Could even add an operation parameter to let the caller
    select actions,
    [...]
    operation in ['auto', 'run', 'edit', 'display', 'browse',
    'explore', 'share', 'send', 'like', 'email', 'open', 'xdg-open',
    ...] # can be incrementally added/implemented

    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".

    ----------


    Python tracker <report@bugs.python.org>
    <http://bugs.python.org/issue3177\>


    @larryhastings
    Copy link
    Contributor

    In Linux we could try nautilus then Mozilla
    (file://.../containing_folder) then fall back to a shell
    cd && ls if no browser is available, raising NotImplemented
    if all else fails... until someone implements for that
    user's platform particulars.

    What if they're using a KDE-based system? Or XFCE? Or xmonad? Or qtile?
    What if their preferred file browser is Midnight Commander?
    What if they use Chrome? Or Konqueror? Or Opera? Or Lynx?

    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".

    @HobsonLane
    Copy link
    Mannequin

    HobsonLane mannequin commented May 29, 2012

    Then they or we can add Konqueror, etc to the options that are 'try'ed for
    the 'view' option. Worst case they get a terminal cat of the file instead
    of an error message or unintended action (like running a script). What
    exactly are you proposing to do when execute is False? That's exactly what
    the augmented launch function would do when the "open" or "view" option is
    chosen. Anything else for the other options like "edit" or "email" is just
    gravy (for application developers targeting the 90% of platforms that we
    could easily anticipate).

    Gnome/KDE Xdg-open, Android "Share", Windows Open, already do implement
    more than we need. Like your "execute" flag, I'm suggesting we bypass all
    that unpredictable open() stuff and allow the application developer to
    chose what happens when they launch a file. It would allow the app
    developer to stay out of the tug-of war between PC manufacturers, OS
    manufacturers, and Internet service providers trying to set "preferred
    applications" and steering customers to their products.

    But clearly you have passion for the boolean execute flag, and you probably
    have more experience with python standard libraries than I do, so go for
    it. I like your "execute" option. Just thought you'd like to extend it to
    include other options. I'm happy with my cross-platform home-rolled
    launch() function for my apps that can't afford to leave launch() actions
    up to chance.

    On Sat, May 26, 2012 at 4:00 PM, Larry Hastings <report@bugs.python.org>wrote:

    Larry Hastings larry@hastings.org added the comment:

    In Linux we could try nautilus then Mozilla
    (file://.../containing_folder) then fall back to a shell
    cd && ls if no browser is available, raising NotImplemented
    if all else fails... until someone implements for that
    user's platform particulars.

    Designing a cross-platform API based on abilities provided by only one of
    those platforms, then waiving your hand and saying , 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".

    ----------


    Python tracker <report@bugs.python.org>
    <http://bugs.python.org/issue3177\>


    @merwok
    Copy link
    Member

    merwok commented May 29, 2012

    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.

    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.

    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.)

    @merwok
    Copy link
    Member

    merwok commented May 29, 2012

    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').

    @merwok
    Copy link
    Member

    merwok commented May 29, 2012

    (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.)

    @HobsonLane
    Copy link
    Mannequin

    HobsonLane mannequin commented May 29, 2012

    shutil.whatever with os.startfile(..., 'edit').

    That's why I thought an 'auto' option might be useful--to allow the caller
    to indicate that they want to respect the OS settings for open/run, if
    possible.

    @ronaldoussoren
    Copy link
    Contributor

    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:

    • The patch implements "open" (default), "edit", "print" and
      "explore" actions.

    • "open" and "explore" work fine

    • "edit" does not work as I'd expect because the function used
      to retrieve the default editor for a file returns the default
      application that can open the file, not necessarily one that
      claims to be an editor for the file-type. As an example,
      'os.startfile("file.html", "edit")' starts Safari instead of
      an editor.

    • "print" works for some file type, but not others (depending on
      whether or not the target application implements the required
      AppleScript API)

    • "execute" is untested, I haven't found an application yet that claims
      this role.

    Other notes:

    • This patch links Python with the ApplicationServices framework
      (which contains the LaunchServices framework)

    • Because the API uses Apple's application frameworks users might get
      annoying crashes when calling this function in a subprocess.

    • The code attached code is a prototype, it should be correct w.r.t.
      resource management, but isn't fully baked yet. One example of that
      is the use of a generic RuntimeError exception to signal problems,
      others are the lack of documentation and tests.

    @danpla
    Copy link
    Mannequin

    danpla mannequin commented Jan 17, 2018

    tl;dr: shutil.whatever should call startfile(..., 'edit') or xdg-open; other actions implemented by startfile are not cross-platform.

    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.

    @amirjn
    Copy link
    Mannequin

    amirjn mannequin commented Feb 3, 2018

    same problem here

    1 similar comment
    @amirjn

    This comment was marked as duplicate.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    @dbarnett
    Copy link

    dbarnett commented Sep 15, 2024

    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.

    @merwok
    Copy link
    Member

    merwok commented Sep 16, 2024

    I don’t think there is a well thought-out proposal, or a big need for this.
    Third-party modules can fill the need.

    @merwok merwok closed this as not planned Won't fix, can't repro, duplicate, stale Sep 16, 2024
    @dbarnett
    Copy link

    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?

    @dbarnett
    Copy link

    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.

    @zed
    Copy link

    zed commented Nov 8, 2024

    Often shutil_open = webbrowser.open works.

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    stdlib Python modules in the Lib dir type-feature A feature request or enhancement
    Projects
    None yet
    Development

    No branches or pull requests

    10 participants