Get script's location when called by another app? - python

How can my Python script reliably get its own location so it can write a file next to itself?
I made a Python script that writes a new file with some working statistics:
import os
NAME = __file__.split('/')[-1]
PATH = os.path.dirname(os.path.realpath(__file__))
PROPER_PATH = os.path.join(MY_PATH, MY_NAME)
with open(os.path.join(PATH, STATS_NAME), 'wb') as statsfile:
statsfile.write(mystats)
This works great and I can call it from anywhere and it writes the stats file in the same place, next to the script. Apart from when I call the script from a macro VBA script.
The script gets called okay but writes its data to:
"C:\Users\lsp\AppData\Roaming\Microsoft\Templates"
How do I make sure it writes to the correct path (same as the script path)?
As a work-around I can imagine giving the path as an argument and then providing it in the VBA but surely there's a Pythonic way to achieve the behavior?

Related

Python - File Path not found if script run from another directory

I'm trying to run a script that works without issue when I run using in console, but causes issue if I try to run it from another directory (via IPython %run <script.py>)
The issue comes from this line, where it references a folder called "Pickles".
with open('Pickles/'+name+'_'+date.strftime('%y-%b-%d'),'rb') as f:
obj = pickle.load(f)
In Console:
python script.py <---works!
In running IPython (Jupyter) in another folder, it causes a FileNotFound exception.
How can I make any path references within my scripts more robust, without putting the whole extended path?
Thanks in advance!
Since running in the console the way you show works, the Pickles directory must be in the same directory as the script. You can make use of this fact so that you don't have to hard code the location of the Pickles directory, but also don't have to worry about setting the "current working directory" to be the directory containing Pickles, which is what your current code requires you to do.
Here's how to make your code work no matter where you run it from:
with open(os.path.join(os.path.dirname(__file__), 'Pickles', name + '_' + date.strftime('%y-%b-%d')), 'rb') as f:
obj = pickle.load(f)
os.path.dirname(__file__) provides the path to the directory containing the script that is currently running.
Generally speaking, it's a good practice to always fully specify the locations of things you interact with in the filesystem. A common way to do this as shown here.
UPDATE: I updated my answer to be more correct by not assuming a specific path separator character. I had chosen to use '/' only because the original code in the question already did this. It is also the case that the code given in the original question, and the code I gave originally, will work fine on Windows. The open() function will accept either type of path separator and will do the right thing on Windows.
You have to use absolute paths. Also to be cross platform use join:
First get the path of your script using the variable __file__
Get the directory of this file with os.path.dirname(__file__)
Get your relative path with os.path.join(os.path.dirname(__file__), "Pickles", f"{name}_{date.strftime('%y-%b-%d')}")
it gives you:
with open(os.path.join(os.path.dirname(__file__), "Pickles", f"{name}_{date.strftime('%y-%b-%d')}"), 'rb') as f:
obj = pickle.load(f)

Trying to open a certain filetype using my python exe

I made a simple python file encryptor. I want to open all encrypted (or .aes) files using my program. I have already made it into a working executable using pyinstaller but when I tried to open a .aes file with my program (through the context menu in the file explorer), it unsurprisingly started the program but did not take the .aes file as input. I want to open any .aes file using my program and for the python script to somehow get the filepath as a variable. Is there any way of doing this when working with pyinstaller?
I want something like:
if isencrypted(filepath):
decrypt(filepath)
The file name will be passed as a command line argument. You need
import sys
filepath = sys.argv[1]

My python application creates an html file but when I freeze it with pyinstaller it does not create anything, why?

I've made a program that simply creates an HTML file in the location where the Python script is executed.
The problem is that after having freeze my application with PyInstaller, the executable no longer creates the HTML file as intended but does nothing. (It only makes the print statement of my program appear.)
Is there a way to get around this?
*I used the open("x.html","w+") function to create the HTML file.
The code that creates the HTML looks like this:
def create_html():
f = open("x.html", "w+")
f.write("<!DOCTYPE html>\n<html>\n<body>\n")
f.close()
I think what would fix the problem is to create the html file outside the working directory. However I have no idea how to do that.
After looking around I've found that my program does indeed work but creates the html file in the home directory on my mac. Is there a way to change that?
You can try
f = open ("x.html", "wb")
or if you need to add at the end of the file you can use
f = open ("x.html", "a")
although you can also use py2exe to generate an executable.
Are you sure your code is open("x.html", "w+") instead of open("/x.html", "w+")?
Try to use
open("./x.html", "w+") and try again.
You should learn about absolute path and relative path.
After looking around I've found that my program does indeed work but creates the html file in the home directory on my mac.
Is your program install in home directory?

Python create a directory and save .txt file into it

I am currently working on the school assignment using the Python Socket library.
I have server.py and client.py, and basically, I request a copy of the .txt file from client-side to server-side, and client-side needed to receive the text elements, create a new .txt file and directory to save it.
I am stuck in the file handling on the client-side.
What is the best way I can do create a directory and save .txt file into it?
# create a new .txt file for incoming data and save to new directory
with open(new_dir / "copied_text_file.txt", '+w') as text:
text.write(file_text)
I tried this way, and it does not save in my new directory.
I appreciate your help, thank you!
If you are trying to create a path, use os.path methods, see in particular join.
Is the name of your new directory "new_dir"? If so the command needs to be open("new_dir/copied_text_file.txt", "+w"). If not and new_dir is a string of the directory use open((new_dir + "/copied_text_file.txt"), "+w") better yet would be to use os.path.join(new_dir, "copied_text_file.txt") and call open on the resulting pathname.
open() takes a string as the destination for the file you're going
to be working with. You can pass it a URI just like would would use
when working on the command line.
import os
with open(os.path.join(new_dir / "copied_text_file.txt", '+w')) as text:
text.write(file_text)
You could just concatenate with +,
with open(new_dir+'/'+ "copied_text_file.txt", '+w')) as text:
# ...
However, using + will be lower because path.join lives inside
complied c code the python interpreter has an easier time running,
rather than having to do the concatenation in python which has more
overhead than CPython models.
https://docs.python.org/3.5/library/os.path.html#os.path.join

Python import module load_source

I have question on the load_source.
when my 2 .py files are in the same directory /home/pi they work fine.
main.py
#!/usr/bin/python
import buttonlog
buttonlog.py
import datetime
i = datetime.datetime.now()
#OPEN FILE & APPEND
f=open('buttonlog.txt','a')
#WRITE DATE THEN NEW LINE WITH THE '\N'
f.write(i.isoformat() + '\n')
When I run python main.py it writes an entry like I'd expect.
However I'd like to store main.py in another directory so I tried this, it is stored in the /home/pi/test
#!/usr/bin/python
import imp
imp.load_source('buttonlog', '/home/pi/buttonlog.py')
When I run python /home/pi/test/main.py I do not get any errors nor does it write an entry into my file. What am I doing wrong?
The secret is the use of the open command.
As the documentation says about the first argument,
file is a path-like object giving the pathname (absolute or relative to the current working directory) of the file to be opened or an integer file descriptor of the file to be wrapped.
By passing just "buttonlog.txt", this is not an absolute pathname, so it's relative to the current working directory.
The simplest way to fix this is to use a full path. If you always want it writing in to /home/pi, you just need:
f=open('/home/pi/buttonlog.txt','a')
There are other alternatives, though I think this is the cleanest. You could also change your current working directory prior to issuing the open command for the same results. Simply put this code above the open line:
import os
os.chdir("/home/pi")

Categories