Editing Paths in Python - python

I'm trying to create a program that duplicates itself to another location and creates a batch file on the desktop. I can make it duplicate itself and I can create the batch file but I need some help with the paths.
I can find the path that my program is currently in. Both the direct path and the path to the directory. My problem lies in the fact that I want to place the file in (let's just say for simplicity) 'C:\Users\Me\Documents'. How would I edit the path? I want to be able to place this on a generic windows computer so I can't hard code the path in because each user will be different. This goes the same for placing the batch file and setting it for the right directory to run the python script in documents.
I have tried both
import os
print os.path.dirname(os.path.abspath(__file__))
and
import os
print os.path.abspath(__file__)
but am clueless as to how to edit the path. When I try googling for it and searching this wonderful site, all I get is stuff about configuring the Python path on windows and other stuff that I can't quite understand at my current level of Python.
Now I turn to you, can you help? Any input would be appreciated, if you could explain how it worked that would be even better!
<>
Due to some questions about my code (and a specific one to post it) Here it is
from sys import argv # Imports module
import os
script, create = argv # Gets script name and desired amount of copies
data = open(script) # Creates a variable to store the script
indata = copy.read() # Creates the data to be copied from the script
batData = """
echo off
%s
""" % # This is not finished, creating that batch file
createT = int(create) + 1
for i in range(1, createT): # Runs a set amount of times
copyName = "%s.py" % str(i) # Creates the name for the file
copy = open(copyName, 'w+') # Opens/creates the file for editing
copy.write(indata) # Writies the indata to the file opened
copy.close # Closes that file
batName = "%s.bat" % str(i)
bat = open(batName, 'w+')
It is not finished but hopefully you get the gist. The argv at the beginning is so I can change the amount of copies made, that will be deleted later as I evolve the code but for now I like it there.
I have currently tried the following to find the path:
import os
print os.path.abspath(__file__)
print os.path.dirname(os.path.abspath(__file__))
print os.path.dirname(__file__)
test = os.path.dirname(__file__)
a, b, c, d, e, f, g, h, i = test.split("\\")
print c
What I want to happen (or think I want to happen) is for the path to be found, then split into pieces (either each directory or break off everything after the username). Then I want to append the document folder tag to the end. For the batch instead of the document tag it will be for the desktop.
among a few others that people have posted. I hope this helps!

Your code snippet returns a string. Just take that string and edit to make the path you want. I'm on a mac so I can't test with an actual windows directory but I'll try to make it look Windows-ish. For instance, lets say this code:
directory_path = os.path.dirname(os.path.abspath(__file__))
print(directory_path)
gives you:
C:\Users\username\AppData
You can use the split function to break the path into pieces (docs).
stuff = path_string.split('\')
print(stuff)
Code output:
['C:', 'Users', 'username', 'AppData']
You can use the pieces create the path you want and then use it to write the file. So, if you want the username folder just loop until you find it. Some example code is below (just an example to get you started - read up on Python if you need help understanding the code).
username = ""
for i in range(0, len(stuff)):
if stuff[i] == "Users":
username = stuff[i + 1]
Not sure if that answers your question but hope it helps.

Am I correct that you are trying to figure out how to make a file path to some location on the user's directory without knowing who the user is going to be that is executing the program?
You may be able to take advantage of environment variables for this. For instance, I can get a file path to the C://Users/username/ directory of whoever is executing the code with:
my_path = os.path.join("C:\\", "Users", os.getenv("USERNAME"))
Where os.getenv("USERNAME") returns the value of the USERNAME environment variable (should be the name of the user that is currently logged on).
You can list all of the available environment variables and their values in Python with:
for key, val in os.environ.items():
print("{} \t {}\n".format(key, val)) # or however you want to prettify it
You may get lucky and find an environment variable that gives you most of the path to begin with.
In general, os.path.join(), os.path.relpath(), and os.path.abspath() combined with some environment variables might be able to help you. Check out the os documentation and os.path documentation.

Many times when modifying a path, I am looking to add/remove folders. Here is my simple method for adding a path, e.g. if I want to move the path of an object into a folder added_path='/train/.
Since my paths are usually uniform, I check the last split characters in the first file location. Usually, my experience is that windows have \\ at the end while Mac and Linux have `/', which makes this work across operating systems. (note: if the paths are not uniform, you obviously place the if-else in the for-loop.)
if '\\' in data[0].img_file:
split_char = '\\'
else:
split_char = '/'
for img in data:
img_path = img.img_file.split(split_char)
label_path = img.label_file.split(split_char)
img.img_file = '/'.join(img_path[:-1]) + added_path + img_path[-1]
img.label_file = '/'.join(label_path[:-1]) + added_path + label_path[-1]
So, this for loop uses all the folders up until the file name, where we insert the extra folder, and then, at last, add the file name.
Example input path: 'data/combined/18.png'
Example output path: 'data/combined/train/18.png'

Related

How do I put a variable in a path

I'm trying to store a path in a variable. see below
target = r"C:\Users\User\CodeProjects\WebSafer"
However, I need it to be dynamic. Not hardcoded to my username, so I get the login username by doing:
val = os.getlogin()
So I need to put the variable val in the path. But every time I tried doing it I always get a truncating/syntax error. Please help me! Below is the code snippet:
print("No copy found...making a copy\n")
val = os.getlogin()
original = r"C:\*******\********\*******\***\****"
target = r"C:\Users\User\CodeProjects\WebSafer"
shutil.copy(original, target)
The "*" are just for privacy reasons, there actually replaced with the right path location to what I'm copying.
What I have tried so far:
target = r"C:\Users\{val}\CodeProjects\WebSafer".format(val = os.getlogin)
target = r"C:\Users\{}\CodeProjects\WebSafer".format(val)
target = rf"C:\Users\{val}\CodeProjects\WebSafer".format(val = os.getlogin)
target = rf"C:\Users\{}\CodeProjects\WebSafer".format(val)
Don't mix f with .format, this is working for me:
import os
val = os.getlogin()
print(rf"C:\Users\{val}\CodeProjects\WebSafer")
And I think better way is:
import os.path
from pathlib import Path
home = str(Path.home())
print(os.path.join(home, "CodeProjects\WebSafer"))
Then if you encounter some error when copying, you need clarify what you want to copy, copy a file, or a folder, if a folder, should it go to within the dest folder, or overwrite dest folder?
You may want try different methods such as
shutil.copy, shutil.copytree, and different parameters.
"r" means the string will be treated as raw string so try removing that and using escaped characters target = "C:\\Users\\{val}\\CodeProjects\\WebSafer".format(val = os.getlogin)
You can use f-string (to directly enter your variable) and r-string (to enter the path without escape characters like \) together.
val = os.getlogin() # Returns username
target = fr"C:\Users\{val}\CodeProjects\WebSafer"
If you're getting a No such file or directory error, it means that the actual file or folder does not exist. Check the actual path to ensure every part (Your Username, CodeProjects, Websafer) exists on your computer.
In case you don't know if your user will have that folder on their system, you can use a try-except block to alert the user or to revert to some default folder instead.

How to correctly apply a RE for obtaining the last name (of a file or folder) from a given path and print it on Python?

I have wrote a code which creates a dictionary that stores all the absolute paths of folders from the current path as keys, and all of its filenames as values, respectively. This code would only be applied to paths that have folders which only contain file images. Here:
import os
import re
# Main method
the_dictionary_list = {}
for name in os.listdir("."):
if os.path.isdir(name):
path = os.path.abspath(name)
print(f'\u001b[45m{path}\033[0m')
match = re.match(r'/(?:[^\\])[^\\]*$', path)
print(match)
list_of_file_contents = os.listdir(path)
print(f'\033[46m{list_of_file_contents}')
the_dictionary_list[path] = list_of_file_contents
print('\n')
print('\u001b[43mthe_dictionary_list:\033[0m')
print(the_dictionary_list)
The thing is, that I want this dictionary to store only the last folder names as keys instead of its absolute paths, so I was planning to use this re /(?:[^\\])[^\\]*$, which would be responsible for obtaining the last name (of a file or folder from a given path), and then add those last names as keys in the dictionary in the for loop.
I wanted to test the code above first to see if it was doing what I wanted, but it didn't seem so, the value of the match variable became None in each iteration, which didn't make sense to me, everything else works fine.
So I would like to know what I'm doing wrong here.
I would highly recommend to use the builtin library pathlib. It would appear you are interested in the f.name part. Here is a cheat sheet.
I decided to rewrite the code above, in case of wanting to apply it only in the current directory (where this program would be found).
import os
# Main method
the_dictionary_list = {}
for subdir in os.listdir("."):
if os.path.isdir(subdir):
path = os.path.abspath(subdir)
print(f'\u001b[45m{path}\033[0m')
list_of_file_contents = os.listdir(path)
print(f'\033[46m{list_of_file_contents}')
the_dictionary_list[subdir] = list_of_file_contents
print('\n')
print('\033[1;37;40mThe dictionary list:\033[0m')
for subdir in the_dictionary_list:
print('\u001b[43m'+subdir+'\033[0m')
for archivo in the_dictionary_list[subdir]:
print(" ", archivo)
print('\n')
print(the_dictionary_list)
This would be useful in case the user wants to run the program with a double click on a specific location (my personal case)

Why does my loop work correctly on the first iteration but not on the full set I'm looping through?

I am trying to rename folders in bulk based on the folders/files contained within them (then moving the image files at the end of each path into they're respective model/color folder directories).
Each folder/file has a similar naming convention of MODEL_COLOR.
The code below works, though seems to only be working correctly on the first folder, in other words, the folders are being renamed correctly but the last leg of code seems to be taking the folder which contains the images and moves it to the corresponding path, instead of specifically moving the images to the corresponding path and dropping the folder they're originally in.
On the first folder the loop iterates, it actually moves the images to the correct Model > Color directory, though on all folders after that it seems to be moving the folder containing the images into the correct Model > Color directory, instead of just moving the images alone into the corresponding directory.
After looking at the forums I've seen similar issues where when changing the directory or deleting certain instances, the loop can't iterate correctly due to the initial set changing during the looping process (i.e. deleting or renaming part of the path while iterating). I'm pretty sure it's a simple fix but I can't seem to find the solution that'll work best.
Standard FolderNames:
CL4003IN_45F
CL4003IN_56F
CL40157U_01D
CL40157U_52H
import glob, os, shutil
folder = 'C:\\testing'
# create new folder directory based on Model/Color [is working, but moves file_path into base directory]
# arr = []
for file_path in glob.glob(os.path.join(folder, '*_*')):
new_dir = file_path.rpartition('_')[0]
new_subdir = file_path.rpartition('_')[2]
try:
os.mkdir(os.path.join(new_dir, new_subdir))
except WindowsError:
# Handle the case where the target dir already exist.
pass
shutil.move(file_path, os.path.join(new_dir, new_subdir))
# arr.append(file_path)
Completing the iteration of glob before the loop by storing it in a list helped avoid some unwanted errors.
#...
for file_path in list(glob.glob(os.path.join(folder, '*_*')
...#
But by modifying my code and removing the following from the loop:
try:
os.mkdir(os.path.join(new_dir, new_subdir))
except WindowsError:
pass
Allowed the code to iterate through all the folders in the directory without transferring the folder before the file into the new_dir > new_subdir directory.
The new code that works across a multitude of folders within a directory is:
import glob, os, shutil
folder = 'C:\\testing'
# create new folder directory based on Model > Color
for file_path in list(glob.glob(os.path.join(folder, '*_*'), recursive=True)):
new_dir = file_path.rpartition('_')[0]
new_subdir = file_path.rpartition('_')[2]
shutil.move(file_path, os.path.join(new_dir, new_subdir))
This may not be the most efficient code (and may not work across all instances, that remains to be determined!), though definitely works as intended for now.
Special thanks to those that helped with their suggestions.
The reason why you don't see the actual error is that you catch too many errors in your except: statement.
You intend to catch FileExistsError, so you should also just look for this one. Otherwise you would have noticed that the code in fact throws a FileNotFoundError.
The reason for that is that os.mkdir does not automatically create parent directories. It only creates directories one layer deep, but your code requires two layers of new directories.
For that, you would have to use os.makedirs(...) instead. Conveniently, os.makedirs also accepts an exist_ok flag, which gets rid of your entire try:-except: construct.
A further annotation is that you have a lot of duplicate calculations in your code:
file_path.rpartition('_') gets calculated twice
os.path.join(new_dir, new_subdir) gets calculated twice
I'd suggest storing those in meaningful variables. This speeds up your code, makes it more readable and more maintainable.
Here's a reworked version of your code:
import glob
import os
import shutil
folder = 'C:\\testing'
for file_path in glob.glob(os.path.join(folder, '*_*')):
(new_dir, _, new_subdir) = file_path.rpartition('_')
target_path = os.path.join(new_dir, new_subdir)
os.makedirs(target_path, exist_ok=True)
shutil.move(file_path, target_path)
Further improvements/fixes
There's still a bunch wrong in your code:
You don't check if the thing found by glob is a file
Splitting by _ does not stop at the directory divider. This means that something like C:\\my_files\\bla will get split into C:\\my and files\\bla.
I think you didn't care about either of those, because you thought 'the user would not use the script like this'. But this is actually a case that will happen:
You have a file C:\\my_files\\CL4003IN_45F, which will get moved to C:\\my_files\\CL4003IN\\45F\\CL4003IN_45F, as expected.
You run the script again. The script will find C:\\my_files\\CL4003IN. It doesn't check if it's a folder, so it will process it anyway. It will then split it into C:\\my and files\\CL4003IN.
The entire folder C:\\my_files\\CL4003IN will get moved to C:\\my\\files\\CL4003IN. Therefore the original file CL4003IN_45F ends up in C:\\my\\files\\CL4003IN\\CL4003IN\\45F\\CL4003IN_45F
The solution is:
only use rpartition on the filename, not the entire path
check if it actually is a file, or a directory
Most of these tasks get solved easier with pathlib. I took the liberty of rewriting your code and fixing those issues:
from pathlib import Path
folder = Path('C:\\testing')
for file_path in folder.iterdir():
file_name = file_path.name
# Ignore directories
if not file_path.is_file():
continue
# Split the filename by '_'
(target_dir, _, target_subdir) = file_name.rpartition('_')
# If no '_' found, ignore
if not target_dir:
continue
# Compute the target path and create it if necessary
target_path = folder / target_dir / target_subdir
target_path.mkdir(parents=True, exist_ok=True)
# Move the file to its new position
target_file_path = target_path / file_name
file_path.rename(target_file_path)
One last remark: folder.iterdir() actually does return an iterator. But that shouldn't be a problem in this case, as we explicitely check if the path is an existing file and not a directory or something that already got deleted. But if you want to be 100% safe write list(folder.iterdir()).

Writing Data to File then Moving to Desktop

I'm sure this is really simple but I can't figure out how to make a parsed result into its own file then move it to my desktop using python. Here is my code so far. I just want to save the result "names" as its own file then move it to my desktop but I can't find the answer anywhere. Is this an uncommon practice?
from gedcom.element.individual import IndividualElement
from gedcom.parser import Parser
import os
import shutil
import pickle
# Path to your `.ged` file
file_path = '/Users/Justin/Desktop/Lienhard_Line.ged'
# Initialize the parser
gedcom_parser = Parser()
# Parse your file
gedcom_parser.parse_file(file_path, False)
root_child_elements = gedcom_parser.get_root_child_elements()
# Iterate through all root child elements
for element in root_child_elements:
# Is the `element` an actual `IndividualElement`? (Allows usage of extra functions such as `surname_match` and `get_name`.)
if isinstance(element, IndividualElement):
# Get all individuals whose surname matches "Doe"
if element.surname_match('Lienhard'):
# Unpack the name tuple
(first, last) = element.get_name()
names = (first + " " + last)
pickle.dumps(names)
Saving a file to one location and then moving it is to another not how it's usually done, no. Just save to the final location.
from pathlib import Path
pic = Path.home() / 'Desktop' / 'names.pickle'
with pic.open('w') as picklefile:
pickle.dump(names, picklefile)
The pathlib library makes working with file names somewhat easier than the old os.path API, though both are in common use.
Writing and then renaming has some uses; if you need to absolutely make sure your data is saved somewhere, saving it to a temporary file until it can be renamed to its final name is a fairly common technique. But in this case, saving to a different location first seems like it would only introduce brittleness.
The above assumes that you have a directory named Desktop in your home directory, as is commonly the default on beginner-oriented systems. To be perfectly robust, the code should create the directory if it doesn't already exist, or perhaps simply save to your home directory.
Indeed, a much better convention for most purposes is simply to always save in the current directory, and let the user take it from there. Hardcoding paths in another directory just adds confusion and uncertainty.
with open('names.pickle', 'w') as picklefile:
pickle.dump(names, picklefile)
This code creates a file called names.pickle in the invoking user's current working directory. The operating system provides the concept of a current working directory for precisely this purpose. Perhaps see also Difference between ./ and ~/ if you need more details about how this works.

Trying to create a replay feature for a Turn-based game (very newbie)

I'm currently playing a trading card game called Hearthstone which is made by blizzard. The game is pretty good, but lacks basic features that any game that calls itself "competitive" should have, like stat tracking and replay.
So as I said in the title, I'm trying to create a (very crude and poorly done) script that let's me record every match I play. Due to my lack of programming skills, 80% of the script is just a bunch of code that I borrowed from all sorts of places and adapted to make it do what I wanted.
The idea is to make it work like this:
I take a picture of every turn I play. It might become annoying, but I do not dare to think about implementing OCR as to make the script take a picture at the start of every turn by itself. Would be awesome but I just can't do it...
The game sends every picture to the desktop (no need to code that).
At the end of the game I run the script
2.1 Every match is going to have a numbered folder so the script creates that. The folders are going to be called "Match1", "Match2", etc. You can see how poorly written that is because I made it on my own :P
import sys
import os
import shutil
def checkFolder():
os.path.join('D:\Hearthstone\Replays\Match1')
matchNumber=1
while os.path.exists("D:\\Hearthstone\\Replays\\Match"+ str(matchNumber)) is True:
matchNumber=matchNumber + 1
else:
os.makedirs("D:\Hearthstone\Replays\Match"+str(matchNumber))
2.2 Script sends the photos from Desktop to the recently created folder. The Problem is that I do not know how to make the script change the destination folder to the newest folder created. I did not write this part of the code, i merely adapted it. Source: http://tinyurl.com/srcbh
folder = os.path.join('C:\\Users\\Felipe\\', 'Desktop') # Folder in which the images are in.
destination = os.path.join('D:\\Hearthstone\\Replays\\', 'match9999') #**Destination needs to be the newest folder and I dont know how to implement that...
extmove = 'png' # The extension you wish to organize.
num = 0 # Variable simply to use after to count images.
for filename in os.listdir(folder): #Run through folder.
extension = filename.split(".")[-1] # This strips the extensions ready to check and places into the extension
if extension == extmove: # If statement. If the extension of the file matches the one set previously then..
shutil.move(folder + "\\" + filename, destination) # Move the file from the folder to the destination folder. Also previously set.
num = num + 1
print(num)
print (filename, extension)
And that's it! I need help with step 2.2. I'd certainly appreciate the help!
Now, the reason I made such a big post is because I wanted to expose my idea and hopefully inspire someone to take on a similar project seriously. Hearthstone has thousands of players that could benefit from it, not to mention that this seems to be a fairly easy task to someone with more experience.
Ok, I finally got it to work!
import sys
import os
import shutil
def sendPhotos():
matchNumber=1
photos_dest = "D:\\Hearthstone\\Replays\\Match"
while os.path.exists(photos_dest+ str(matchNumber)): #creates the name of the folder "Match1", "Match2", etc.
matchNumber=matchNumber + 1
else:
photos_destination = photos_dest+str(matchNumber)
os.makedirs(photos_destination)
for files in os.listdir('C:\\Users\\Felipe\\Desktop'):#only png files are moved
if files.endswith(".png"):
shutil.move(files, photos_destination)
sendPhotos()
Thank you to those who gave me some answers! I really appreciated it!
Well, first off, the fact that you identified a problem and put together a solution shows that you certainly don't lack programming skills. Give yourself some credit. You just need more practice. :)
This should be better, I haven't run it, so there might be some errors :P
def checkFunction(base_dir='D:\\Hearthstone\\Replays\\'): #set this as a parameter with a default
match_number = 1
if os.path.exists(base_dir): #if the base directory doesn't exist you'll have a bad time
while os.path.exists(os.path.join(base_dir, 'Match{0}'.format(match_number)))
match_number += 1
new_dir = os.path.join(base_dir, 'Match{0}'.format(match_number))
os.makedirs(new_dir)
return new_dir
For the function checkFolder, I suggest having it return the new directory name (as above). You'll also need to indent all the lines below it so python knows those lines are part of that function (this might just be a formatting issue on SO though).
Then, once the checkFolder function is working properly, all you have the change in 2.2 is:
destination = checkFolder()
This sees which matches are in the folder and takes the lowest number for the folder.
folder = os.path.join('C:\\Users\\Felipe\\', 'Desktop') # Folder in which the images are in.
recorded_matches_location = 'D:\\Hearthstone\\Replays\\'
match_number = 1
match_name = 'match1'
while match_name in os.listdir(recorded_matches_location):
match_number = 1 + match_number
match_name = 'match' + str(match_number) # corrected it! there must be a string and not a variable
destination = os.path.join(recorded_matches_location, match_name) #**Destination needs to be the newest folder and I dont know how to implement that...
extmove = 'png' # The extension you wish to organize.
num = 0 # Variable simply to use after to count images.
for filename in os.listdir(folder): #Run through folder.
extension = filename.split(".")[-1] # This strips the extensions ready to check and places into the extension
if extension == extmove: # If statement. If the extension of the file matches the one set previously then..
shutil.move(folder + "\\" + filename, destination) # Move the file from the folder to the destination folder. Also previously set.
num = num + 1
print(num)
print (filename, extension)

Categories

Resources