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

Idle: run from editor without explicit save #63242

Closed
terryjreedy opened this issue Sep 17, 2013 · 9 comments
Closed

Idle: run from editor without explicit save #63242

terryjreedy opened this issue Sep 17, 2013 · 9 comments
Labels
3.10 only security fixes topic-IDLE type-feature A feature request or enhancement

Comments

@terryjreedy
Copy link
Member

BPO 19042
Nosy @terryjreedy, @serwy
Files
  • ScriptBinding.diff.zip
  • ScriptBinding.py
  • 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 = <Date 2020-06-07.21:57:41.675>
    created_at = <Date 2013-09-17.17:20:54.009>
    labels = ['expert-IDLE', 'type-feature', '3.10']
    title = 'Idle: run from editor without explicit save'
    updated_at = <Date 2020-06-07.21:57:41.673>
    user = 'https://github.com/terryjreedy'

    bugs.python.org fields:

    activity = <Date 2020-06-07.21:57:41.673>
    actor = 'terry.reedy'
    assignee = 'none'
    closed = True
    closed_date = <Date 2020-06-07.21:57:41.675>
    closer = 'terry.reedy'
    components = ['IDLE']
    creation = <Date 2013-09-17.17:20:54.009>
    creator = 'terry.reedy'
    dependencies = []
    files = ['45309', '45328']
    hgrepos = []
    issue_num = 19042
    keywords = []
    message_count = 9.0
    messages = ['197997', '197998', '259951', '279898', '279903', '279921', '279961', '296498', '370913']
    nosy_count = 6.0
    nosy_names = ['terry.reedy', 'bsherwood', 'roger.serwy', 'THRlWiTi', 'Bruce.Sherwood', 'perilbrain']
    pr_nums = []
    priority = 'normal'
    resolution = 'out of date'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue19042'
    versions = ['Python 3.10']

    @terryjreedy
    Copy link
    Member Author

    The General tab of the IDLE Preferences dialog has this section with two radiobuttons:

    Autosave Preferences
    At Start of Run (F5) () Prompt to Save () No Prompt

    The latter option actually means "No prompt unless the window is a new window ('Untitled') that has never been saved." In the latter case, there is a prompt anyway.

    This issue proposes that the current No Prompt option be more truthfully labelled and that a true no-prompt option be added. Currently in .cnf configuration files, autosave = 0 or 1. The new option would be 2 ('really true' ;-).

    The motivation is to provide a default scratch file for throwaway code and thereby encourage more use of the editor even for single multi-line statements, which are harder to edit in the Shell than single-line statements. The behavior of Untitled windows would not otherwise change.

    ----------------
    Possible dialog forms

    Autosave Preferences
    When Running (F5) a window with unsaved changes, prompt to save
    () Always () Only for Untitled () Never

    I find it slightly confusing that Autosave yes is selected in the negative with 'no prompt'. An alternative:

    Autosave Preferences
    When Running (F5) a window with unsaved changes,
    autosave instead of prompting to save
    () Never () Always except for Untitled () Always

    Another alternative is to retitle the section

    Prompt to Save versus Autosave
    When Running (F5) a window with unsaved changes, prompt to save
    () Always () Only for Untitled () Never
    --------------

    However the dialog is worded, the new option would mean that unsaved new windows would be autosaved on Run to .idlerc/untitled.py. (I picked this directory because it already contains 4 per-user files, including recent-files.lst, and has to be writable.) This would be a common scratch file for all Untitled windows.

    The particular Untitled window saved should not be 'associated' with that path. Its title would not change to 'untitled.py'. Idle allows only one edit window per disk file (path) but allows multiple Untitled windows not associated with any path.

    The 'unsaved' flag on the window would not be cleared, so that closing the window would still bring up the 'save this untitled window' message box. (The alternative would be to clear it, but unclear it if any other Untitled window were saved.)

    The full path to untitled.py would be added (or moved) to the top of the Recent Files list, just as with any other file. The file could then be retrieved (if not overwritten) in a later session. This would be particularly useful if running it caused a crash or freeze.
    ------------------

    This proposal is based on Bruce Sherwood's vague description of a feature coded by G. Polo. I could not find a tracker issue for it. One apparent difference is that I propose adding a third option to the existing set instead of a new binary option. I have no idea whether Polo proposed to silently save the file (as I propose) or add a mechanism to truly run without saving (which is how a user will see it unless they look at Recent Files). In any case, my proposal is for a pretty minimal change. I believe it would take extra code to prevent the Recent Files listing.

    @terryjreedy terryjreedy added topic-IDLE type-feature A feature request or enhancement labels Sep 17, 2013
    @BruceSherwood
    Copy link
    Mannequin

    BruceSherwood mannequin commented Sep 17, 2013

    Very nice, Terry. Good point about positive vs. negative specifications. I think maybe your "Prompt to Save versus Autosave" is the best scheme, because one is specifying whether or not to do something "active" (namely, put up a save dialog).

    @terryjreedy
    Copy link
    Member Author

    bpo-1326830 ('python scratchpad', rejected) was about same issue, though less specific about the implementation.

    A different solution to the multiline statement problem would be to separate Shell into the read-only history (with prompts in a sidebar, so normal 4 space indents would work) and an active mini-editor entry box that would continue to submit without saving.

    @terryjreedy
    Copy link
    Member Author

    Duplicate bpo-28581 has a autosave patch in the initial post, which points out that "We are often required to copy code from various sites say some tutorial or code samples which are good for one time usage."

    It prompted me to think more about the idea to 'add a mechanism to truly run without saving [to disk]', which I mentioned in my first post above. To run editor code, IDLE retrieves the code from the Text widget as a single strings; runs compile(code, 'filename', 'exec'), ships the code object to the user process, and runs exec(code, fakemain). (I understand this much better now than 3 years ago.) The only use of the disk copy is for tracebacks. But is it *needed*? I believe not. Here is standard interactive Python (3.5.2):

    >>> def f():
    	1/0
    	
    >>> f()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 2, in f
    ZeroDivisionError: division by zero
    >>>

    Here is the same traceback in IDLE's Shell:

    Traceback (most recent call last):
      File "<pyshell#40>", line 1, in <module>
        f()
      File "<pyshell#39>", line 2, in f
        1/0
    ZeroDivisionError: division by zero

    IDLE has filled in the missing lines from Shell's text. I have not yet tracked where this is done (in pyshell.py, I believe), but I presume it could do the same for Untitled editor windows. For this to work, the window titles should be Untitled-0, Untitled-1, ... . The specific name used in the compile call would appear in the traceback, to be used to lookup the window the code came from.

    With the ability to run the whole buffer without saving, it would be easy to run a selection. (There have been multiple requests for this.)

    IDLE already asks about saving when one tried to close a window with unsaved text, so there is no need to force saving when running for this purpose.

    @terryjreedy terryjreedy added the 3.7 (EOL) end of life label Nov 2, 2016
    @perilbrain
    Copy link
    Mannequin

    perilbrain mannequin commented Nov 2, 2016

    "To run without saving" was the first idea I got, but it was difficult to pursue in first phase. After your hints I think I have achieved what you have described.

    I have done a few fixes(luckily in a single file ScriptBinding.py) which are giving satisfactory result.

    I am attaching the original, modified and the diff version of files named ScriptBindingOriginal.py, ScriptBinding.py and ScriptBinding.diff respectively.

    Here is what I am getting with the new code
    Traceback (most recent call last):
      File "*Untitled*", line 3, in <module>
      File "*Untitled*", line 2, in f
    ZeroDivisionError: division by zero

    This is the summary of the patch (idle-python3.4)

    +import io
    ....

    #====== One member in class for reducing annoyance ====

            self.no_save = False

    #====== New definition for function tabnanny======

    -    def tabnanny(self, filename):
    +    def tabnanny(self, source):
    +        f = io.StringIO(source)# , os.linesep  *****Maybe*****
    -        with tokenize.open(filename) as f:

    #====== Added 2 functions =========

        def source_from_file(self, filename):
            with open(filename, 'rb') as f:
                source = f.read()
            if b'\r' in source:
                source = source.replace(b'\r\n', b'\n')
                source = source.replace(b'\r', b'\n')
            if source and source[-1] != ord(b'\n'):
                source = source + b'\n'
            return source
        
        def source_from_editor(self):
            self.editwin.io.fixlastline()
            source = self.editwin.text.get("1.0", "end-1c")
            return source

    #====== New definition for function checksyntax======

    • def checksyntax(self, filename):
      + def checksyntax(self, source, filename):

    #====== Changes in function run_module_event (Main) ==========

        def _run_module_event(self, event):
            filename = self.getfilename()
            filename = self.getfilename()
            if not filename:
                self.no_save = True
                source = self.source_from_editor()
                filename = self.editwin.top.wm_title()
            else:
                source = self.source_from_file(filename)
                self.no_save = False
            code = self.checksyntax(source, filename)
            if not code:
                return 'break'
            if not self.tabnanny(source):
                return 'break'
            interp = self.shell.interp
            if PyShell.use_subprocess:
                interp.restart_subprocess(with_cwd=False)
            if not self.no_save:
               ....
            interp.runcode(code)
            return 'break'

    #====== Finally suppressing the annoyance ======

                if autosave and filename:
                    self.editwin.io.save(None)
                elif self.no_save:
                    filename = None
                else:
                    confirm = self.ask_save_dialog()

    Please have a review and let me know if it can solve this issue.

    @terryjreedy
    Copy link
    Member Author

    Looks promising. The second half of a patch would be to fill in the missing lines.

    Before I review, please sign a CA. The online form at https://www.python.org/psf/contrib/contrib-form/ takes about 5 minutes.

    @perilbrain
    Copy link
    Mannequin

    perilbrain mannequin commented Nov 3, 2016

    I have signed the CA. Meantime a came across one more problem in function tabnanny(encoding issue) so I am attaching the new version of ScriptBinding.py.

    Summary:-

        def tabnanny(self, source, encoding = None):
            # XXX: tabnanny should work on binary files as well
            #print(encoding)
            f = io.StringIO(source.decode(encoding) if encoding else source)
    
    def source_from_file(self, filename):
            with open(filename, 'rb') as f:
                source = f.read()
                encoding = tokenize.detect_encoding(f.readline)[0]
            ....
            return source, encoding
    
    def _run_module_event(self, event):
            ...
            if not filename:
                self.no_save = True
                source = self.source_from_editor()
                filename = self.editwin.top.wm_title()
            else:
                source, encoding = self.source_from_file(filename)
                self.no_save = False
            code = self.checksyntax(source, filename)
            if not code:
                return 'break'
            if not self.tabnanny(source, encoding):
                return 'break'
            ....

    @terryjreedy
    Copy link
    Member Author

    I rewrote title to slightly generalize, and add and document here on the tracker additional variations that have been requested or that I have thought of.

    1. Autosave <untitled> to somewhere, like .idlerc/untitled.py

    2. Run without saving, by stuffing code lines into the line cache, as done with lines entered into Shell. Use <unsaved> rather than <PyShell> as pseudo file name.

    Either 1 or 2 has been requested multiple times. It is part of the GPolo megapatch of bpo-10079 that adds or changes about 10 features at once. I do not know at the moment if it implemented 1 or 2. msg149930 says "This functionality is scattered across several files."

    Some other ideas:

    1. Run without restarting. This *seems* easy. Requests:
      https://stackoverflow.com/questions/43814027/how-to-prevent-python-idle-from-restarting-when-running-new-script
      https://stackoverflow.com/questions/41926321/run-a-module-in-idle-python-3-4-without-restart

    2. Run selection. This seems more useful without restart. I would make it default if there *is* a selection. (Alt-F5?, context menu?

    3. Run as input. Print prompt and statement; run as if entered; add to history. Requires parsing into statements, which is not trivial. Compile with mode=statement has 3 returns: error, partial statement so far okay, success. Anything after 1st statement is ignored. This blocks pasting multiple lines. But provides alternative for entering multiline statments.

    4. Run with 'tracing -t'. Print line# and statement. Similar to autostep in debugger.

    5. Run with debugger, without having to turn it on in the Shell that may not exist.

    6. Run with command line arguments bpo-5680.

    7. Run with PYTHONSTARTUP or IDLESTARTUP file.

    We cannot have menu options for each run option, let alone combinations of options. I am thinking of adding one 'Custom Run' or 'Run w/ Options' or ??? menu entry and a dialog box with the F5 settings the initial defaults. The setting for a particular file should be persistent, and can be across IDLE sessions, as with debug breakpoints.

    I intend to open a separate issue for
    10. Run in a console, detached, with output to the console.
    This should probably have a separate 'Run in Console' menu entry and options box.

    @terryjreedy terryjreedy changed the title Idle: add option to autosave 'Untitled' edit window Idle: run from editor without explicit save Jun 20, 2017
    @terryjreedy
    Copy link
    Member Author

    bpo-5680 June 2019 also added no restart. Some other ideas are possible additions in new issues.

    @terryjreedy terryjreedy added 3.10 only security fixes and removed 3.7 (EOL) end of life labels Jun 7, 2020
    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.10 only security fixes topic-IDLE type-feature A feature request or enhancement
    Projects
    None yet
    Development

    No branches or pull requests

    1 participant