Search code examples
pythongoogle-cloud-platformcroncron-task

Chron - [Permission Denied] when I try to save & remove file from directory


Issue: Unable to save file in directory (/root/Notion/Image) when using Cron schedule

This is what my code is trying to do:

  1. Check email
  2. Download image attachment
  3. Store in a directory - root/Notion/Image
  4. Retrieve file path

The script is working when I run it manually in Google Cloud terminal. The problem is when I try to schedule it on Cron, it's unable to access the folder to save the file locally.

This is the error when the script failed and require permission:

Traceback (most recent call last):
  File "Notion/test.py", line 121, in <module>
    path = get_attachments(email.message_from_bytes(msg[0][1]))
  File "Notion/test.py", line 47, in get_attachments
    with open(filePath, 'wb') as f:
PermissionError: [Errno 13] Permission denied: '/root/Notion/Image/3.jpeg'

This is the code to retrieve attachment from email

    def get_attachments(msg):
        for part in msg.walk():
            if part.get_content_maintype()=='multipart':
                continue
            if part.get('Content-Disposition') is None:
                continue
    
            fileName = part.get_filename()
    
            if bool(fileName):
                filePath = os.path.join(attachment_dir, fileName)
                with open(filePath, 'wb') as f:
                    f.write(part.get_payload(decode=True))
                    return str(filePath)

Resolved: The problem is that I shouldn't use root directory since it requires permission. I've changed it to home directory instead.

attachment_dir = '/home/dev_thomas_yang/folder_name/folder_name'

For people who needs to check their home direction, simply run this script.

    from pathlib import Path
    home= str(Path.home())
    
    print(home)

Thanks Triplee for the patience to breakdown my issue despite my sloppy ways of presenting it!


Solution

  • The easiest fix hands down is to change the code so it doesn't try to write to /root. Have it write to the invoking user's home directory instead.

    Your question doesn't show the relevant parts of the code, but just change attachment_dir so it's not an absolute path. Maybe separately take care of creating the directory if it doesn't already exist.

    import pathlib
    # ...
    attachment_dir = pathlib.Path("cron/whatever/attachments").mkdir(parents=True, exist_ok=True)
    # ...
    for loop in circumstances:
        get_attachments(something)
    

    A better design altogether would be to have get_attachments accept the directory name as a parameter, so you can make this configurable from the code which calls it. Global variables are a nuisance and cause hard-to-debug problems because they hide information which is important for understanding the code, and tricky to change when you try to debug that code and don't know which parts of the code depend on the old value.