Requirements
Python, internet, time
Problem
Goal
The script should simply download the latest wordpress zip file, unzip it and allow the user to set the main wp-config values.
Pseudo-code
- Make a get request ( https://wordpress.org/latest.zip )
- Save the downloaded zip file in the current directory
- Create a wp-config.php file based off wp-config-sample.php file
- Ask the user for the wp-config values
Solution
Miscellaneous functions – Path handling
def checkpathexists(path: str) -> bool: return os.path.exists(path) def deletedir(path: str): print("deleting directory: {}".format(path)) return shutil.rmtree(path) def deletefile(path: str): print("deleting file: {}".format(path)) return os.remove(path)
These are functions that only play minor roles in the code logic.
Check if the path exists?
The first function uses the os.path module’s existing method, which checks if the given path exists. We will use this method to confirm if the wordpress zip file or wordpress zip folder exists. The method’s return value is of type boolean.
os.path.exists(path: str) -> bool
Delete directory and file
In case of an already existing wordpress.zip or wordpress folder, either of the functions deletedir or deletefile are to be applied. The os module also provides a remove method which, according to its documentation, can remove a file based on the provided file path. Note that if the path given points to a folder/directory, an error will be thrown. Also make sure the file isn’t open while running the script due to permission issues.
os.remove(path, *, dir_fd: int) -> None
While one could use the os.removedirs method to remove directories recursively, I decided to use the shutil module’s rmtree method. After reading here, you’ll see the former would throw an error if the directory isn’t empty. The latter actually does what we want.
Formatting strings using variables
To learn more about cool ways to generate and format strings in python, do some exercises here.
Making Requests – Download WordPress Zip File
def download_wordpress(): print("Wordpress download initiating...") if checkpathexists("wordpress.zip"): delete = input("should I delete your wordpress zip file?: y/n ") if delete == "y": deletefile("wordpress.zip") else: print("the script failed, because you won't let me delete stuff. hmpf.") exit() url = "https://wordpress.org/latest.zip" with requests.get(url, stream=True) as r: r.raise_for_status() with open("wordpress.zip", "wb") as f: shutil.copyfileobj(r.raw, f) print("Wordpress download completed...") return
Getting input from user
The python’s input function takes in a string as an argument and returns the user’s input provided over the command line.
input(prompt)
Fetching data over http
In JS, making http requests can be done by axios or the fetch function. In python, the requests module offers methods with which http requests are sent..Since I am simply fetching data and not sending one, the GET verb should suffice. Learn more about http verbs here.
The response object returned from the get call contains the downloaded wordpress zip file needed. Just in case the wordpress file is large, we will enable sending the large file in chunks rather than all at once by setting stream to true. Read here for more
With.. as statement
This is based on my understanding a cleaner and safer way to properly close an actions involving file handling and data manipulation. Read
Manipulating files – Unzipping zip files
def unzip_wordpress(): print("Wordpress unzipping initiating...") if checkpathexists("wordpress"): delete = input("should I delete your wordpress folder?: y/n ") if delete == "y": deletedir("wordpress") else: print("the script failed, because you won't let me delete stuff. hmpf.") exit() with zipfile.ZipFile("wordpress.zip", "r") as zip_ref: zip_ref.extractall(".") print("Unzipping wordpress file completed....") return
Zip up!
Module here.
File handling – wp-config.php from wp-config-sample.php
def duplicate_file(source, destination): shutil.copyfile(source, destination) print("Copying file {} to {} was successful...".format(source, destination)) return
After unzipping the wordpress file and you move into the folder, you should notice that the main wp-config.php is absent. Since the wp-config-sample.php file is identical to wp-config.php, we only need to duplicate and rename the file.
Functions and I/O : Configure wp-config.php via command line
def replace_line_in_source(findstr, replacement, filepath): with open(filepath, "rt") as f: data = f.read() data = data.replace(findstr, replacement) f.close() with open(filepath, "wt") as fw: fw.write(data) fw.close() print("The line {} was replaced with {} in file {}".format( findstr, replacement, filepath)) return
The replace line source function looks for a given string findstr in a given file (at filepath) and when found, replaces the found string with the given replacement.
Default Configuration
def fill_wp_config(): config = { "define( 'DB_NAME', 'database_name_here' )": {"field": "DB_NAME", "replacement": "database_name_here"}, "define( 'DB_USER', 'username_here' )": {"field": "DB_USER", "replacement": "username_here"}, "define( 'DB_PASSWORD', 'password_here' )": {"field": "DB_PASSWORD", "replacement": "password_here"}, "define( 'DB_HOST', 'localhost' )": {"field": "DB_HOST", "replacement": "localhost"}, "define( 'DB_CHARSET', 'utf8' )": {"field": "DB_CHARSET", "replacement": "utf8"}, "define( 'DB_COLLATE', '' )": {"field": "DB_COLLATE", "replacement": ""} } for key in config: definition = config[key] field = definition["field"] replacement = definition["replacement"] user_replacement = input( "Please type in the replacement for [" + field + "] or press ENTER to type nothing: ") if user_replacement and len(user_replacement) > 0: definition["replacement"] = user_replacement r = definition["replacement"] valid_replacement = "define( '{}', '{}' )".format(field, r) replace_line_in_source(key, valid_replacement, "wordpress/wp-config.php") print("All fields replaced") return
I have a feeling there exists a better way to implement this logic than this overly-complicated blob. However, with my mediocre programming background this was what I could come up with. Apologies.
Simply, I stored the 6 important configurations in wp-config.php in a dictionary; loop over the keys and ask the user for their corresponding values. If the user provides a value, it is replaced. Else, the default value is retained.
Run Program
def start(): download_wordpress() unzip_wordpress() duplicate_file("wordpress/wp-config-sample.php", "wordpress/wp-config.php") fill_wp_config()
Question
- How would you create a function that generates a mysql database automatically and specifically for the WordPress project ?
Author Notes
I learned a lot of basic python syntax by means of this exercise. If you have suggestions, please type them below. Thanks a lot.
Links
- https://requests.readthedocs.io/en/master/
- https://docs.python.org/3/tutorial/errors.html
- https://stackoverflow.com/questions/16694907/download-large-file-in-python-with-requests
- https://stackoverflow.com/questions/3451111/unzipping-files-in-python
- https://www.geeksforgeeks.org/python-shutil-copyfile-method/
- https://pythonexamples.org/python-replace-string-in-file/
- https://www.w3schools.com/python/python_lists.asp
- https://realpython.com/iterate-through-dictionary-python/
- https://www.geeksforgeeks.org/taking-input-from-console-in-python/
- https://www.w3schools.com/python/ref_string_split.asp
- https://linuxize.com/post/python-delete-files-and-directories/
- https://stackoverflow.com/questions/19782075/how-to-stop-terminate-a-python-script-from-running/34029481
- https://docs.python.org/2/library/os.path.html
- https://docs.python.org/2/library/webbrowser.html
- https://thispointer.com/python-how-to-delete-a-directory-recursively-using-shutil-rmtree/