Posts: 3
Threads: 1
Joined: Oct 2020
Hello first post... as a Python user.
A long time PHP coder, trying to make the move over to Django, so I figured I would try my hand at a backup script to get my feet wet with Python. I have played with Python here and there, but this is my first serious attempt at an actual script that I plan to use on a daily bases.
Here my script:
#!/usr/bin/python3*
'''
Module name:
Author:
Contact:
Desc:
'''
__version__ = '0.0.1'
import shutil
import os, os.path
import datetime
TODAYS_DATE = str(datetime.date.today())
NUMBER_OF_BACKUP = 3
BACKUP_LOCATION = '/home/Superusr/Backup_Scripts/Test_01/Save_Location/'
FOLDER2BACKUP = '/home/Superusr/Backup_Scripts/Test_01/Sample_Folder'
CURRENT_BACKUP_NAME = FOLDER2BACKUP + "_" + TODAYS_DATE
CURRENT_LIST_OF_BACKUPS = []
MOST_RECENT_BACKUP = "Newest"
OLDEST_BACKUP = "Oldest"
def GET_MOST_RECENT_BACKUP_NAME():
print("Step 2...")
for names in os.listdir(BACKUP_LOCATION):
CURRENT_LIST_OF_BACKUPS.append(names)
CURRENT_LIST_OF_BACKUPS.sort()
def SET_MOST_RECENT_BACKUP():
print("Step 3...")
MOST_RECENT_BACKUP = CURRENT_LIST_OF_BACKUPS[-1]
print(MOST_RECENT_BACKUP)
def SET_OLDEST_BACKUP():
print("Step 4...")
OLDEST_BACKUP = CURRENT_LIST_OF_BACKUPS[0]
print(OLDEST_BACKUP)
def INITIAL_SETUP():
print("Step 1...")
GET_MOST_RECENT_BACKUP_NAME()
SET_MOST_RECENT_BACKUP()
SET_OLDEST_BACKUP()
def main():
INITIAL_SETUP()
print("Most recent backup is: {}".format(MOST_RECENT_BACKUP))
if __name__ == "__main__": main() With a result of:
Output: Step 1...
Step 2...
Step 3...
Sample_Folder_2020-09-15
Step 4...
Sample_Folder_2020-09-01
Most recent backup is: Newest
------------------
(program exited with code: 0)
Press return to continue
So yes, this is a basic script, I am building it up, but that is the puzzling part. I put the print statements ("print('Step 1...')" in to make sure each function was being called to troubleshoot.
Originally, without the print statements, and the initial variables set to blank ("MOST_RECENT_BACKUP = ''") it was coming back blank with no errors. So after setting an initial value to the variables, and comparing that to the results after each step I realised my variables were being reset to initial setting even though they were defined out of scope. I was thinking it possibly could be because a "global variables" problem vs. a local variable, but the "CURRENT_LIST_OF_BACKUPS" variable is outside, and is initialised and is maintained through the parsing of the script, so I don't know why "MOST_RECENT_BACKUP" and "OLDEST_BACKUP" are not maintaining their new values and are returning to their initial value instead.
I am sure it is a simple oversight on my part, but I honestly don't see it.
Any help would be appreciated, thanks in advance.
OS: Ubuntu 20.04
Python Version: Python 3.8.2
Posts: 8,163
Threads: 160
Joined: Sep 2016
Oct-06-2020, 05:00 PM
(This post was last modified: Oct-06-2020, 05:00 PM by buran.)
because they have local scope inside the function. you need to declare them as global, using global keyword if you want to change the global name.
Note, if you don't assign to a name inside the function, it will use the global one, without need to declare it global
e.g.
def SET_MOST_RECENT_BACKUP():
print("Step 3...")
global MOST_RECENT_BACKUP
MOST_RECENT_BACKUP = CURRENT_LIST_OF_BACKUPS[-1]
print(MOST_RECENT_BACKUP) CURRENT_LIST_OF_BACKUPS is a list and lists are mutable, that's why it "works" for it.
Note that using globals is generally considered bad, pass arguments to functions and return from functions.
Also, using ALL_CAPS for function and names is not consistent with PEP8 recommendations.
Posts: 1,583
Threads: 3
Joined: Mar 2020
Recommended python style is to reserve UPPERCASE names for constants. CURRENT_BACKUP_NAME and GET_MOST_RECENT_BACKUP_NAME() aren't constants.
By default variables assigned in a function are local to that function. Unless you mark the variable as global, you're creating a new variable, setting it, then throwing it away when the function exits. The global variable isn't touched.
$ cat scopeit.py
name = "outside" # this is a global variable
def change_name():
name = "inside" # this is a local variable
print(f"my name in the function is {name}")
change_name()
print(f"my name outside the function is {name}") Output: my name in the function is inside
my name outside the function is outside
Posts: 8,163
Threads: 160
Joined: Sep 2016
To clarify my point
spam = 'some spam'
eggs = 'some eggs'
foo = 'some foo'
def func():
global eggs
eggs = 'new eggs'
spam = 'new spam'
print(f'inside func, spam:{spam}, eggs:{eggs}, foo:{foo}')
print(f'before calling func, spam:{spam}, eggs:{eggs}, foo:{foo}')
func()
print(f'after calling func, spam:{spam}, eggs:{eggs}, foo:{foo}') Output: before calling func, spam:some spam, eggs:some eggs, foo:some foo
inside func, spam:new spam, eggs:new eggs, foo:some foo
after calling func, spam:some spam, eggs:new eggs, foo:some foo
Posts: 3
Threads: 1
Joined: Oct 2020
Oct-06-2020, 05:58 PM
(This post was last modified: Oct-06-2020, 06:06 PM by p2bc.)
Ok so I get all that, and changed my script, and yes it works. but I don't like it. BUT is works.
#!/usr/bin/python3*
'''
Module name:
Author:
Contact:
Desc:
'''
__version__ = '0.0.1'
import shutil
import os, os.path
import datetime
TODAYS_DATE = str(datetime.date.today())
NUMBER_OF_BACKUP = 3
BACKUP_LOCATION = '/home/Superusr/Backup_Scripts/Test_01/Save_Location/'
FOLDER2BACKUP = '/home/Superusr/Backup_Scripts/Test_01/Sample_Folder'
CURRENT_BACKUP_NAME = FOLDER2BACKUP + "_" + TODAYS_DATE
CURRENT_LIST_OF_BACKUPS = []
def GET_MOST_RECENT_BACKUP_NAME():
print("Step 2...")
for names in os.listdir(BACKUP_LOCATION):
CURRENT_LIST_OF_BACKUPS.append(names)
CURRENT_LIST_OF_BACKUPS.sort()
def SET_MOST_RECENT_BACKUP():
print("Step 3...")
RESULT = CURRENT_LIST_OF_BACKUPS[-1]
return RESULT
def SET_OLDEST_BACKUP():
print("Step 4...")
RESULT = CURRENT_LIST_OF_BACKUPS[0]
return RESULT
def INITIAL_SETUP():
print("Step 1...")
GET_MOST_RECENT_BACKUP_NAME()
def main():
INITIAL_SETUP()
MOST_RECENT_BACKUP = SET_MOST_RECENT_BACKUP()
OLDEST_BACKUP = SET_OLDEST_BACKUP()
print("Most recent backup is: {}".format(MOST_RECENT_BACKUP))
if __name__ == "__main__": main() I don't like it because it doesn't future proof the code, I know what I want to do next and I will have to switch it up.
Like, just moving "MOST_RECENT_BACKUP = SET_MOST_RECENT_BACKUP()" and "OLDEST_BACKUP = SET_OLDEST_BACKUP()"
out of the main() but after INITIAL_SETUP() function breaks it.
I guess it is a classic case of trying to run before you can walk.
Thanks again for your help.
After reviewing it some more, I understand why is breaks when the variable are breaking outside the main() function, because the intial_setup() function only get called then. Therefore not set before then.
Posts: 6,806
Threads: 20
Joined: Feb 2020
If you don't like global variables how about using a class? Then the global variables become instance variables. This also forces you to do initial setup because that code can be placed in a special method named __init__ that runs immediately after an instance is created.
import os
class Backups:
def __init__(self, backup_location=None):
'''Called when a new instance is created'''
if backup_location is None:
backup_location = os.getcwd()
self._backup_location = backup_location
self._index = 0
self._backups = []
self.update()
def update(self):
self._backups = os.listdir(self._backup_location)
self._backups.sort()
def __getitem__(self, index):
'''Used for list type indexing. instance[3]'''
return self._backups[index]
def __iter__(self):
'''With __next__ makes this class iterable. for item in instance:'''
self._index = 0
return self
def __next__(self):
'''With __iter__ makes this class iterable. for item in instance:'''
if (i := self._index) < len(self._backups):
self._index += 1
return self._backups[i]
raise StopIteration
def newest(self):
return self._backups[-1]
def oldest(self):
return self._backups[0]
def __len__(self):
'''Return number of backups for len(instance).'''
return len(self._backups)
if __name__ == "__main__":
backups = Backups())
print(f'Newest backup is: {backups.newest()}')
print(f'Oldest backup is: {backups.oldest()}')
for backup in backups:
print(backup)
print(f'There are {len(backups)} backups')
if len(backups) > 2:
print(f'Backup[2] = {backups[2]}') Output: Newest backup is: sticks.py
Oldest backup is: __pycache__
__pycache__
allwidgets.py
class_instance_var_test.py
console.py
interactiveconsole.py
junk.py
monkeypatching.py
pythonhighlighter.py
snail.py
sticks.py
There are 10 backups
Backup[2] = class_instance_var_test.py
I don't understand what it is you are trying to do, but hopefully this gives you a peek into the kinds of things you can do with Python. And I am a noob. I'm sure this could be done much better.
Posts: 3
Threads: 1
Joined: Oct 2020
@ deanhystad
I saw your post before before all the turmoil from earlier in the week. It is funny, I was always intending to develop the script into a more robust solution, which of course would include OOP ("class") in someway. If you look at my original code, you would see classic OOP structuring with "get 'ers and set 'ers". I guess after so many year is just instinct.
Anyways, after your comment I looked at python classes, and structuring my original code in a class format, it work almost from the beginning, but also was more inline with what I was expecting.
#!/usr/bin/python3
'''
Module name:
Author: p2bc
Author: p2bc
Contact:
Desc: Backup script that can be automatic/manual with predefined or dynamic locations
'''
__version__ = '0.0.2'
import shutil
import os, os.path
import datetime
class BackUp:
number_of_backups = 3
backup_location = '/home/superusr/Backup_Scripts/Test_02/Save_Location/'
folder_to_backup = '/home/superusr/Backup_Scripts/Test_02/Sample_Folder'
current_list_of_backups = []
oldest_backup = ""
newest_backup = ""
def __init__(self, today_date = "NULL", number_backups = "NULL", backup_location = "NULL", folder_backup = "NULL", name_backup = "NULL"):
self.todays_date = str(datetime.date.today()) if today_date == "NULL" else today_date
self.number_of_backups = BackUp.number_of_backups if number_backups == "NULL" else number_backups
self.backup_location = BackUp.backup_location if backup_location == "NULL" else backup_location
self.folder_to_backup = BackUp.folder_to_backup if folder_backup == "NULL" else folder_backup
self.current_backup_name = self.folder_to_backup + "_" + self.todays_date if name_backup == "NULL" else name_backup
self.get_most_recent_backups()
self.oldest_backup = self.set_oldest_backup()
self.newest_backup = self.set_newest_backup()
def get_most_recent_backups(self):
for names in os.listdir(self.backup_location):
self.current_list_of_backups.append(names)
self.current_list_of_backups.sort()
def set_oldest_backup(self):
return self.current_list_of_backups[0]
def set_newest_backup(self):
return self.current_list_of_backups[-1]
def main():
a_backup = BackUp()
print(a_backup.newest_backup)
print(a_backup.todays_date)
print(a_backup.current_backup_name )
print(a_backup.number_of_backups)
b_backup = BackUp("2008-06-04",5)
print(b_backup.newest_backup)
print(b_backup.todays_date)
print(b_backup.current_backup_name )
print(b_backup.number_of_backups)
if __name__ == "__main__": main() Anyways, with all that taken care of, I took the rest of the time since to refine it and what have you. Nowhere finish, but at least it is functional.
A sample for those who are interested can be found below. Thank you all that helped with your comments, and advice.
#!/usr/bin/python3
'''
Module name:
Author: p2bc
Author: p2bc
Contact:
Desc: Backup script that can be automatic/manual with predefined or dynamic locations
'''
__version__ = '0.0.3'
import shutil
import os, os.path
import datetime
class BackUp:
auto = "False"
number_of_backups = 3
backup_location = '/home/superusr/Backup_Scripts/Test_03/Save_Location/'
folder_to_backup = '/home/superusr/Backup_Scripts/Test_03/Sample_Folder'
folder_name = ""
current_list_of_backups = []
oldest_backup = ""
newest_backup = ""
def __init__(self, auto = None, today_date = None, number_backups = None, backup_location = None, folder_backup = None, name_backup = None):
self.auto = BackUp.auto if auto == None else auto
self.todays_date = str(datetime.date.today()) if today_date == None else today_date
self.number_of_backups = BackUp.number_of_backups if number_backups == None else number_backups
self.backup_location = BackUp.backup_location if backup_location == None else backup_location
self.folder_to_backup = BackUp.folder_to_backup if folder_backup == None else folder_backup
self.current_backup_name = BackUp.folder_to_backup + "_" + self.todays_date if name_backup == None else name_backup
self.folder_name = self.current_backup_name.split("/")[-1]
self.get_most_recent_backups()
self.oldest_backup = self.set_oldest_backup()
self.newest_backup = self.set_newest_backup()
self.make_backup(self.auto)
def get_most_recent_backups(self):
for names in os.listdir(self.backup_location):
self.current_list_of_backups.append(names)
self.current_list_of_backups.sort()
def set_oldest_backup(self):
return self.current_list_of_backups[0]
def set_newest_backup(self):
return self.current_list_of_backups[-1]
def make_backup(self, auto="FALSE"):
responce = auto.lower()
if self.return_if_true(responce): ## Another way of thinking of this, "Is this True?" Yes it is, therefore it is 'True' that this is 'True'.
self.run_backup(self.auto)
else:
while True:
try:
responce = str(raw_input("Would you like to make a backup... (Yes/No) : "))
if self.return_if_true(responce): ## Another way of thinking of this, "Is this True?" Yes it is, therefore it is 'True' that this is 'True'.
self.run_backup(self.auto)
break
elif self.return_if_false(responce): ## Another way of thinking of this, "Is this False?" Yes it is, therefore it is 'True' that this is 'False'.
print("Thanks for coming, Bye for now!")
break
print("Needs to be a 'Yes' or 'No' answer.")
except Exception as e:
print(e)
def run_backup(self, auto="FALSE"):
if os.path.exists(self.backup_location + self.folder_name):
print(self.return_if_false(auto,"No need to make a backup, there is already a backup for that date."))
else:
try:
shutil.copytree(self.folder_to_backup , self.backup_location + self.folder_name )
print(self.return_if_false(auto,"Congrats you made a backup"))
except Exception as e:
print(e)
@staticmethod
def return_if_true(responce, msg=""): ## Testing for the a certain result , typically the one you expect. In this case "True" or "Yes"
responce = responce.lower() if isinstance(responce, str) else "true"
if responce == "true" or responce == "t" or responce == "yes" or responce == "y":
return msg if len(msg) > 0 else True ## It is "True" because you are testing for a specific result, in this case "True". Think of it as, "It is 'True' that this is 'True'"
@staticmethod
def return_if_false(responce, msg=""): ## Testing for the a certain result , typically the one you expect. In this case "False" or "No"
responce = responce.lower()
if responce == "false" or responce == "f" or responce == "no" or responce == "n":
return msg if len(msg) > 0 else True ## It is "True" because you are testing for a specific result, in this case "False". Think of it as, "It is 'True' that this is 'False'"
def __repr__(self):
return "Backup()"
def __str__(self):
pass
def main():
a_backup = BackUp() #Will prompt the user as a confirmation
b_backup = BackUp("True") #Will run without a prompt, best for when you want to run as a cron job
c_backup = BackUp("", "2019-1-31", 5) #So that you get a prompt, but you change the backup reference to another date, and the number of backs there are to '5'
if __name__ == "__main__": main() Thanks again everyone
|