Python learning on Steroids
Coach Beard Jr
Posted on March 25, 2024
Recently i am going through Network and Security in Python book and wanted to write and test the scripts myself.
Along the way I wanted to learn and code using Vim editor.
So my target is this
Continue learning Vim - there is a great tutorial by fireship (search for "fireship vim tutorial") in youtube I am not giving any details/tricks about Vim, that video explain better than me.
I want to keep all my notes at a single place. In a single file is much better.
Along the way I also want snippets of code in between my notes lets call it NOTES.MD
At the bottom of the page you can find the link to github repository where this is used in practice.
All the things started well, until one moment when i coded my first basic script and then wanted to code the second script and run it.
Essentially I need to create a new file to execute the second piece of code but need to keep my notes in a single file and also need to attach code snippets in NOTES.MD
Its just irritating to create multiple files and also to copy paste the same code snippet in NOTES.MD also its just repetition work, which is one thing i hate to do the most as i am lazy.
Putting on thinking hats
I found a way to solve this problem
The notes -> write them in the comments
All the scripts write them in a function and call the respective function to execute the respective method/function/script
An example will be like this
import os
'''
os.walk(path)
navigates all the directories in the provided path directory, and
returns three values: _the path directory_, _the names of sub-directories_, _list of filenames in
the current directory path
'''
def os_walk():
file_count = 0
for root, directories, files in os.walk(".", topdown=False):
file_count += len(files)
for file in files:
print(f"[+] {os.path.join(root, file)}")
for sub_folder in directories:
print(f"[+][+] {sub_folder}")
print(f"[=] Total files in the current directory: {file_count}")
if __name__ == '__main__':
os_walk()
Now from the above code snippet, I have my notes about something also the code snippet which i can call from the main method.
If the function needs some arguments then we provide them from the main function as well.
All is well and good to go.
BUT WAIT
We can still enhance this right??
Thinking about this, the problems we face every time we run the new function are
- We need to modify the main function logic to call the necessary function
- Also we have to keep in mind that we might not be knowing before hand the parameters that a function needs. (in my case this is one of the issue, since we are the ones defining the function we might not face this issue, but i want to solve this as well)
Points to note
- User will provide which function to execute
- User will provide necessary arguments/parameters via command line arguments
So the command should be
python main.py -f some_random_function --args "{'args1':'values'}"
This command should execute the some_random_function
with arguments as 'args1' which value is 'values'
To capture the command line arguments lets use optparse
from optparse import OptionParser
def capture_arguments():
parser = OptionParser()
parser.add_option("-f", dest="function", help="Function to execute")
parser.add_option("--args", dest="args", help="Arguments needed for functions to execute", default={})
return parser.parse_args()
Now we have to modify the main method to call the respective function that user mentions via command line arguments
Now to call the main method from the function we need to use the globals
function that is provided by python.
Python globals()
function is a built-in function in Python that returns the dictionary of the current global symbol table.
A symbol table is a data structure that contains all necessary information about the program such as variables, methods, classes etc...
If we want to call a function say sum
we can use globals function to call the sum function as globals()['sum']()
In this manner, we are passing which function to execute via command line arguments which we are capturing via optparse library, which returns the arguments and its parameters.
defined below is the code snippet for the main function which call the function and executes the function that we provide via command line arguments.
if __name__ == '__main__':
(options, args) = capture_arguments()
func = options.function
print(f"[=] Executing {func} function")
globals()[func]()
This is all working good.
What if we need some parameters. Sure we can call input()
function whenever these values are required within the function call itself.
But I want to pass parameters from cli itself. (Another enhancement)
Lets begin another journey.
Since we dont know the amount of arguments and the positional arguments that are required by the function we send "kwargs" to the function as parameters.
def random_function(**kwargs):
#some code
arg = kwargs.get('arg', None)
if __name__ == '__main__':
(options, args) = capture_arguments()
func = options.function
args = options.args
print(f"[=] Executing {func} function")
globals()[func](args)
But the above snippet wont work. The function is expecting dictionary of positional arguments but we giving a string input which then throws TypeError error
To resolve this, lets convert string to dictionary using eval()
function
args = eval(options.args)
This is done, but still we are facing issues that if we run this code we will get error stating that random_function() is not exepecting any positional arguments.
For this we convert the args again as
args = **eval(options.args)
This is similar to
def func(arg1=value1, arg2=value2):
#--- snip----
args = {'arg1':'value1', 'arg2':'value2'}
def func(**args):
#--- snip----
The above code snippets represents they way we can pass the keyword arguments as a dictionary.
so we can modify our main method code as
if __name__ == '__main__':
(options, args) = capture_arguments()
func = options.function
print(f"[=] Executing {func} function")
args = **eval(options.args)
globals()[func](args)
Now still there is one more issue pending,
Not all the functions require arguments to execute the code within it.
So we include try except block in our main method
The function that receive unexpected arguments results in TypeError
error
if __name__ == '__main__':
(options, args) = capture_arguments()
func = options.function
print(f"[=] Executing '{func}' function")
try:
print("[=] Trying to execute function with passing arguments")
globals()[func](**eval(options.args))
except TypeError as error:
print("[=] Executing function without passing arguments")
globals()[func]()
Hence the problem resolved.
Now you can create multiple functions in the same file and pass in the respective method name and arguments to execute the specific section of code.
We can extend this to multiple files as well
Create a sample files called sample.py and sample2.py
And write some dummy functions in both the files. Say def1, def2 in sample.py file and def3, def4 in sample2.py
Now create a main.py file and add the below contents in the file
from sample import *
from sample2 import *
from optparse import OptionParser
def capture_arguments():
parser = OptionParser()
parser.add_option("-f", dest="function", help="Function to execute")
parser.add_option("--args", dest="args", help="Arguments needed for functions to execute", default={})
return parser.parse_args()
if __name__ == '__main__':
(options, args) = capture_arguments()
func = options.function
print(f"[=] Executing '{func}' function")
try:
print("[=] Trying to execute function with passing arguments")
globals()[func](**eval(options.args))
except TypeError as error:
print("[=] Executing function without passing arguments")
globals()[func]()
Now to execute function def1 with arguments val=[1,2], out=False
we can run the command
python main.py -f def1 --args "{'val':[1,2],'out':False}"
There you go.
Some python steroids drugs which you might not need at all in your lifetime. :)
Link to code -> Python_On_Steroids
Posted on March 25, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.