Forum Archive

Extension to save attachment

[deleted]

Simple extension to save attachment

Save.py


# coding: utf-8

import appex, os, console

def save():
    if appex.is_running_extension():
        sFp = appex.get_file_path()
        if sFp:
            console.hud_alert('Saving...')
            with open(sFp, 'rb') as f1:
                with open(os.path.basename(sFp), 'wb') as f2:
                    f2.write(f1.read())
            console.hud_alert('Saved')

if __name__ == '__main__':
    save()
ccc

See @dgelessus comment on how to replace three lines (with, with, write) above with a single call to shutil.copy().

https://forum.omz-software.com/topic/2637/is-it-possible-to-read-a-file-say-txt-file-from-other-app/3

[deleted]

@ccc, thanks...

  • of course the opens are in binary mode so I don't think that part of the comment applies

  • shorter in the source, yes, but longer and so less efficient in execution, shutil copy contains some redundant checks (eg if source and destination files are the same) and triggers a buffered copy in 16k blocks if I'm not mistaken.

dgelessus

I'm not sure why a copy in 16k blocks would be less efficient than reading the whole file into RAM at once... The performance difference should be minimal, and a copy in blocks is much more memory-efficient if the file is very large.

[deleted]

@dgelessus how many times round the write block loop for a 1MB file, 64?

dgelessus

The loop does little more than read a block, check whether there is data left, and write it to the output file. Heck, Unix dd uses 512-byte blocks and it gets its job done quite well. I mean, if you want to worry about that kind of optimization, run some timeits and see how the runtime difference really is. Or write your code in C and let clang optimize it. ;)

Webmaster4o

@dgelessus I think you accidentally changed your status to "away"

dgelessus

@Webmaster4o I always set myself as "away" (for no real reason, except that I don't want to give false impressions that I'm ready to answer when I just left the forum open).

[deleted]

@dgelessus or just avoid shutil copy

smath

I added a folder picker to this to let the user choose where to save the file, but I've run into an issue. The the code runs in pythonista is comes up with a correct path, but when run in the extension it comes up with a completely dirfferent path.

Here's the path to my Documents folder generated in pythonista:

'/private/var/mobile/Containers/Shared/AppGroup/E093AECB-9BC3-4D05-A61B-E336BBA9ADF5/Documents/'

Here's the same path from the extension:

'/private/var/mobile/Containers/Data/PluginKitPlugin/CDEDAE11-0835-44A1-8AA4-F709C76285F9/Documents/'

It's exploring a totally different tree. Because of this the folder picker doesn't populate. It works if I hard code the home directory, but I'd like to know if there's a better way to do it.

The code is here.

dgelessus

Yes, it does look like the HOME environment variable is different in the app extension:

>>> import os
>>> os.environ["HOME"]
'/private/var/mobile/Containers/Data/PluginKitPlugin/E9D61C03-3A12-483F-916C-A77F9C49177F'

Though the cwd is still set to the normal Documents folder:

>>> os.getcwd()
'/private/var/mobile/Containers/Shared/AppGroup/4A0ADB08-4B5E-41F3-96E5-DC5CD6FC939E/Documents'
smath

I've updated it so that is takes the result of os.getcwd and finds the main Documents folder. My way of doing it is probably considered a hack so if there's a better way feel free to let me know.

[deleted]

If wanting to use shutil to block copy the file... this line with copyfileobj should do it more efficiently than copy (avoiding the redundant code in copy and utilising a 512k block size instead of 16k):

#f2.write(f1.read())
shutil.copyfileobj(f1, f2, length=512*1024)