omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    Shortcut to run a script located in Working Copy's dir?

    Pythonista
    2
    12
    270
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • B
      bensaccount last edited by

      This seems like it should be a simple thing, but after searching through these forums, Googling, and asking ChatGPT, I still came up short.

      I'm using Pythonista to run code that's managed by Working Copy, so it's under that app's directory. I'm trying to create a shortcut on the home screen to directly run a script in that directory.

      It seems like there's a couple options here- a pythonista3:// URL opened in Safari with ?action=run, or running Pythonista directly using the Shortcuts app.

      I've found I can get the full directory path with os.path.realpath(__file__), but everything I try just results in Pythonista opening with no scripts open (if I use the URL), or with the last used script open (if I use the Run Script shortcut). It doesn't try to run anything. When I tried it a couple weeks ago, it would give me error messages about the script not being found, but now I'm not even getting those.

      ChatGPT suggested using the Wrench in Pythonista, but it gives me the error message "Script Required: You need to open a Python script in order to generate URLs." A script is already open in the editor, so I'm guessing this only works if the script is located in Pythonista's directory.

      cvp 2 Replies Last reply Reply Quote 0
      • cvp
        cvp @bensaccount last edited by cvp

        @bensaccount said

        I'm guessing this only works if the script is located in Pythonista's directory.

        I think that's true. Perhaps find a way to start a Pythonista script which it-self starts the script of Working Copy.

        In Pythonista V3.3 we were able to start a script running in Python 2 which started a Python 3 interpreter like a wrench script does.

        #!python2
        # force this script to use Python 2 Interpreter so you can start
        # Python 3 Interpreter to run your iCloud script
        from objc_util import *	
        import sys
        arg = sys.argv
        # this script = arg[0]
        script = arg[1]
        path = '/private/var/mobile/Library/Mobile Documents/iCloud~com~omz-software~Pythonista3/Documents/' + script
        arg = arg[2:]	# other arguments
        I3=ObjCClass('PYK3Interpreter').sharedInterpreter()
        print(I3)
        # run a script like in wrench menu (path, args, reset env yes/no)
        #print(path,script)
        I3.runScriptAtPath_argv_resetEnvironment_(path, arg, True)
        
        1 Reply Last reply Reply Quote 0
        • cvp
          cvp @bensaccount last edited by cvp

          @bensaccount as we can't have two times the Pythonista Python 3 interpreter at the same time, I have tested with another thread. Here, an example with (I don't use Working Copy) starting via runScript another script in iCloud

          import sys
          import threading
          
          class my_thread_(threading.Thread):
          	def __init__(self,path,arg):
          		threading.Thread.__init__(self)
          		self.path = path
          		self.arg = arg
          
          	@on_main_thread	
          	def run(self):
          		from objc_util import ObjCClass
          		from time import sleep
          		sleep(1)
          		I3 = ObjCClass('PYK3Interpreter').sharedInterpreter()
          		# run a script like in wrench menu (path, args, reset env yes/no)
          		#print(path,script)
          		I3.runScriptAtPath_argv_resetEnvironment_(self.path, self.arg, True)
          
          arg = sys.argv
          # this script = arg[0]
          script = arg[1]
          path = '/private/var/mobile/Library/Mobile Documents/iCloud~com~omz-software~Pythonista3/Documents/' + script
          arg = arg[2:]	# other arguments
          my_thread = my_thread_(path,arg)
          my_thread.start()
          

          Execute this shortcut

          this shortcut

          B 1 Reply Last reply Reply Quote 0
          • B
            bensaccount @cvp last edited by bensaccount

            @cvp Thanks, having a launcher script in Pythonista's Documents dir fixes the issue! I think starting another interpreter may be unnecessary though, since I do want the script to run inside Pythonista, and using the same version of Python.

            Here's what I ended up using for my launcher script:

            dir = '/private/var/mobile/Containers/Shared/AppGroup/......'
            filename = 'app.py'
            
            import sys
            import os
            
            sys.path.append(dir)
            os.chdir(dir)
            
            with open(filename) as f:
                exec(f.read())
            

            This seems to work fine. And of course this could be altered to take arguments for directory and filename.

            I've run into a couple issues with Shortcuts now:

            1. If I use the Pythonista Run Script shortcut, I have to stop the shortcut manually afterwards, even if Pythonista is closed. Adding "Stop this shortcut" doesn't make a difference. It's not a huge deal, but it may be confusing for the client.
            2. Pythonista will happily run the script multiple times. Is there any way to tell Pythonista not to do this, or do I need to implement something manually, like a lock file? (Or is there a more reliable way to check if a script is running?)

            edit: Ah, I've found TPO's AppSingleLaunch util. Looks like it handles the issues with lock files sticking around when the app doesn't shut down properly, so I may try that.

            cvp 2 Replies Last reply Reply Quote 0
            • cvp
              cvp @bensaccount last edited by

              @bensaccount I didn't know this exec function.... Thanks for the info.

              1 Reply Last reply Reply Quote 0
              • cvp
                cvp @bensaccount last edited by

                @bensaccount I have tried successfully a shortcut with unique action

                alt text

                B 1 Reply Last reply Reply Quote 0
                • B
                  bensaccount @cvp last edited by

                  @cvp That's handy. So a separate launcher script file isn't needed.

                  It can still launch multiple times though. I think I'll have to add TPO's util to my app to prevent that.

                  B 1 Reply Last reply Reply Quote 0
                  • B
                    bensaccount @bensaccount last edited by

                    I'm running into two problems with @TPO's AppSingleLaunch:

                    1. I'm using a NavigationView as my top-level view, which doesn't have a will_close function. It's immutable, so it won't let me monkey patch one in either. Is there another way to detect when the script is exiting?
                    2. I don't think the forced garbage collection is working. If I close the Python script and re-open it (which doesn't delete the lock file due to issue #1), the id it stores- the NavigationView's id- still exists. Even if I wait a while, gc.collect() won't collect it.
                    B 1 Reply Last reply Reply Quote 0
                    • B
                      bensaccount @bensaccount last edited by bensaccount

                      Hm, if I add a will_close method to my root View, it never gets called, I guess that's only called when you use .close().

                      The UIKit docs show a viewWillDisappear method for NavigationView, which does show up in __dir()__ for its objc object. Is there any way to set that via Python?

                      My temporary workaround is hide_close_button=True, so the app can only shutdown properly. But there's still the problem I mentioned earlier of AppSingleLaunch not working due to the NavigationView not getting cleaned up after closing. (edit: Though it's not a problem if the script always closes down properly)

                      cvp 1 Reply Last reply Reply Quote 0
                      • cvp
                        cvp @bensaccount last edited by

                        @bensaccount try to put a close button to top view of NavigationView

                        import ui
                        
                        view = ui.View()
                        navigationView = ui.NavigationView(view)
                        
                        # Add a close button
                        view.left_button_items = [ui.ButtonItem(title="Close", action=lambda x: navigationView.close())] 
                        
                        # Won't show the x-button
                        navigationView.present("sheet", hide_title_bar=True)
                        
                        B 1 Reply Last reply Reply Quote 0
                        • B
                          bensaccount @cvp last edited by

                          @cvp Thanks, looks like we had the same idea. I had just removed the close button, but I have to admit that hiding the title bar is tempting to reclaim some vertical space.

                          cvp 1 Reply Last reply Reply Quote 0
                          • cvp
                            cvp @bensaccount last edited by cvp

                            @bensaccount so you can intercept the root view closing, like a will_close

                            If it is not sufficient, I think that, in Objective C, we could intercept the delegate should_pop of the UINavigationBar, but more complex of course

                            1 Reply Last reply Reply Quote 0
                            • First post
                              Last post
                            Powered by NodeBB Forums | Contributors