Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Solution for Assignment 2.1 #62

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
57 changes: 57 additions & 0 deletions solutions/Pranjal Rai/src/Assignment 1/CommandLineParser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import sys
import json
import re


class CommandLineParser():

def __init__(self):
self.commands = {}

def add_command(self, command_name, format = None, is_required = False, conflicting_command = None, is_flag = False):
command = {'command_name': command_name, 'format': format, 'is_required': is_required, 'conflicting_command': conflicting_command, 'is_flag': is_flag}
self.commands[command_name] = command

def get_arguments(self, argv):
results = {}
commands_found = set()
flag = False

for arg in argv[1:]:
command = None
value = None
argument = arg.split('=')

if len(argument) == 2:
command, value = argument
elif len(argument) == 1:
command = argument[0]
elif len(argument) > 2:
raise Exception('invalid number of arguments passed to ' + argument[0])

commands_found.add(command)

if command not in self.commands: # commands with at least one argument
raise Exception(command + ' is not a recognized command')
else:
if self.commands[command]['is_flag'] is True:
results[command] = True
flag = True
continue
format = self.commands[command]['format']
if re.fullmatch(format, value):
results[command] = value
else:
raise Exception('invalid argument to ' + command)

for command in commands_found:
conflicting_command = self.commands[command]['conflicting_command']
if conflicting_command is not None and conflicting_command in commands_found:
raise Exception('The ' + command + ' and ' + conflicting_command + ' arguments cannot be used together')

for command in self.commands:
instruction = self.commands[command]
if flag is False and instruction['is_required'] == True and command not in commands_found:
raise Exception('The ' + command + ' argument is required, but missing from input')

return results
62 changes: 62 additions & 0 deletions solutions/Pranjal Rai/src/Assignment 1/TestCommandLParser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import unittest
import CommandLineParser as CLP


class TestCommandLParser(unittest.TestCase):

def test_basic_usage(self): #all valid instructions here
parser = CLP.CommandLineParser()
parser.add_command('--key', format = r'\d+', is_required = True)
parser.add_command('--name', format = r'[a-zA-Z]+')
parser.add_command('--local', conflicting_command = '--remote', is_flag = True)
parser.add_command('--remote', conflicting_command = '--local', is_flag = True)

command_line_param = ['./test', '--key=123', '--name=pranjal'] #key and name
result = parser.get_arguments(command_line_param)
self.assertEqual(result, {'--key': '123', '--name': 'pranjal'})


command_line_param = ['./test', '--local'] #test local
result = parser.get_arguments(command_line_param)
self.assertEqual(result, {'--local': True})


command_line_param = ['./test', '--remote'] #test remote
result = parser.get_arguments(command_line_param)
self.assertEqual(result, {'--remote': True})



def test_invalid_usage(self): #all invalid instructions here
parser = CLP.CommandLineParser()
parser.add_command('--key', format = r'\d+', is_required = True)
parser.add_command('--name', format = r'[a-zA-Z]+')
parser.add_command('--local', conflicting_command = '--remote', is_flag = True)
parser.add_command('--remote', conflicting_command = '--local', is_flag = True)

command_line_param = ['./test', '--key=1a3', '--name=pranjal'] #invalid key
with self.assertRaisesRegexp(Exception, 'invalid argument to --key'):
parser.get_arguments(command_line_param)

command_line_param = ['./test', '--key=123', '--name=pranjal123'] #invalid name
with self.assertRaisesRegexp(Exception, 'invalid argument to --name'):
parser.get_arguments(command_line_param)


command_line_param = ['./test', '--name=pranjal'] #missing key
with self.assertRaisesRegexp(Exception, 'The --key argument is required, but missing from input'):
parser.get_arguments(command_line_param)


command_line_param = ['./test', '--remote', '--local'] #local and remote together
with self.assertRaisesRegexp(Exception, 'The --remote and --local arguments cannot be used together'):
parser.get_arguments(command_line_param)


command_line_param = ['./test', '--version'] #unrecognized command
with self.assertRaisesRegexp(Exception, '--version is not a recognized command'):
parser.get_arguments(command_line_param)


if __name__ == '__main__':
unittest.main()
11 changes: 11 additions & 0 deletions solutions/Pranjal Rai/src/Assignment 1/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import CommandLineParser as CLP
import sys


if __name__ == '__main__':
parser = CLP.CommandLineParser()
parser.add_command('--key', format = r'\d+', is_required = True)
parser.add_command('--name', format = r'[a-zA-Z]+')
parser.add_command('--local', conflicting_command = '--remote', is_flag = True)
parser.add_command('--remote', conflicting_command = '--local', is_flag = True)
parser.get_arguments(sys.argv)
45 changes: 45 additions & 0 deletions solutions/Pranjal Rai/src/Assignment 2/BuildAutomationTool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import os
import sys
import json


class BuildAutomationTool():

def execute_command(self, current_working_directory, previous_working_directory, process_command, current_script):

if process_command != "build":
raise Exception(process_command + ' is not a recognized process command')
return
Comment on lines +10 to +12
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this "process_command" thing shouldn't be necessary, if there is only 1 allowed value for this.



absolute_path = os.path.abspath(current_working_directory)
os.chdir(absolute_path)
script_found = False


with open(os.path.join("build.json")) as json_file:
scripts = json.load(json_file)

for script in scripts:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets call these actions, or build-rules, or something.

I recommend creating a separate class for an Action, and maintaining a proper graph, because it will be useful in part3 of the problem.

if script['name'] == current_script:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if I provide current_script = "algorithms/sort_bubble" ? The name in build.json will only be "sort_bubble", and you're not looking in the "algorithms" directory.

script_found = True
if 'deps' in script:
for dependecy in script['deps']:
path = str(dependecy).split('/')
new_script = path.pop()
new_path = ""
for path_segment in path:
new_path += path_segment
self.execute_command(new_path, current_working_directory, process_command, new_script)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to detect circular dependencies and show appropriate error message.

current_command = script['command']
os.system(current_command)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This vaguely satisfied part1 of the problem statement, lets do part2 now.

previous_absolute_path = os.path.abspath(previous_working_directory)
os.chdir(previous_absolute_path)


if script_found is False:
raise Exception('build ' + current_script + ' is not a recognized command')
return

return

53 changes: 53 additions & 0 deletions solutions/Pranjal Rai/src/Assignment 2/BuildAutomationToolTest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import unittest
import BuildAutomationTool as Build
import os
from os import path


class TestBuildAutomationTool(unittest.TestCase):

def test_build_clean(self):
build_automation_tool_object = Build.BuildAutomationTool()
build_automation_tool_object.execute_command(os.getcwd(), os.getcwd(), 'build', 'clean')
current_location = os.getcwd()
absolute_location = os.path.abspath(current_location)
os.chdir(absolute_location)
self.assertEqual(path.exists('test.o'), False)
previous_location = absolute_location
absolute_location += "/algorithms"
os.chdir(absolute_location)
self.assertEqual(path.exists('sort_bubble.o'), False)
self.assertEqual(path.exists('sort_quick.o'), False)
self.assertEqual(path.exists('sort_merge.o'), False)
os.chdir(previous_location)


def test_build_run(self):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of creating new files in the current directory, use tempfile.TemporaryDirectory so that you can run multiple copies of the test in parallel.

build_automation_tool_object = Build.BuildAutomationTool()
build_automation_tool_object.execute_command(os.getcwd(), os.getcwd(), 'build', 'run')
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This API is suboptimal.
The fact that you need to maintain previous_working_directory is an implementation detail, that is an internal problem for the library, and should not affect the API.
Can you do something like:

BuildAutomationTool(root=".").execute("algorithms/sort_bubble")

current_location = os.getcwd()
absolute_location = os.path.abspath(current_location)
os.chdir(absolute_location)
self.assertEqual(path.exists('test.o'), True)
previous_location = absolute_location
absolute_location += "/algorithms"
os.chdir(absolute_location)
self.assertEqual(path.exists('sort_bubble.o'), True)
self.assertEqual(path.exists('sort_quick.o'), True)
self.assertEqual(path.exists('sort_merge.o'), True)
os.chdir(previous_location)


def test_invalid_usage(self):
build_automation_tool_object = Build.BuildAutomationTool()
with self.assertRaisesRegexp(Exception, 'unknown_process_command is not a recognized process command'):
build_automation_tool_object.execute_command(os.getcwd(), os.getcwd(), 'unknown_process_command', 'clean')

with self.assertRaisesRegexp(Exception, 'build unknown_command is not a recognized command'):
build_automation_tool_object.execute_command(os.getcwd(), os.getcwd(), 'build', 'unknown_command')




if __name__ == '__main__':
unittest.main()
21 changes: 21 additions & 0 deletions solutions/Pranjal Rai/src/Assignment 2/algorithms/build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[
{
"name": "clean",
"command": "rm -f *.o"
},
{
"name": "sort_bubble",
"files": ["sort_bubble.cpp"],
"command": "g++ -c sort_bubble.cpp"
},
{
"name": "sort_merge",
"files": ["sort_merge.cpp"],
"command": "g++ -c sort_merge.cpp"
},
{
"name": "sort_quick",
"files": ["sort_quick.cpp"],
"command": "g++ -c sort_quick.cpp"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include <bits/stdc++.h>
using namespace std;

string sort_bubble()
{
return "sort_bubble";
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include <bits/stdc++.h>
using namespace std;

string sort_merge()
{
return "sort_merge";
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include <bits/stdc++.h>
using namespace std;

string sort_quick()
{
return "sort_quick";
}
Binary file not shown.
17 changes: 17 additions & 0 deletions solutions/Pranjal Rai/src/Assignment 2/build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[
{
"name": "clean",
"deps": ["algorithms/clean"],
"command": "rm -f test.o && rm -f test.exe"
},
{
"name": "test",
"files": ["test.cpp"],
"command": "g++ -std=c++11 -c test.cpp"
},
{
"name": "run",
"deps": ["test", "algorithms/sort_bubble", "algorithms/sort_merge", "algorithms/sort_quick"],
"command": "g++ algorithms/sort_bubble.o algorithms/sort_merge.o algorithms/sort_quick.o test.o -o test.exe && ./test.exe"
}
]
11 changes: 11 additions & 0 deletions solutions/Pranjal Rai/src/Assignment 2/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import BuildAutomationTool as Build
import argparse
import os

if __name__ == '__main__':
parser = argparse.ArgumentParser(description = 'Command Line Parser for Build Automation Tool')
parser.add_argument("command", help = "indication to build processes")
parser.add_argument("build_command", help = "different build commands")
args = parser.parse_args()
build_automation_tool_object = Build.BuildAutomationTool()
build_automation_tool_object.execute_command(os.getcwd(), os.getcwd(), args.command, args.build_command)
15 changes: 15 additions & 0 deletions solutions/Pranjal Rai/src/Assignment 2/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <bits/stdc++.h>
using namespace std;

string sort_bubble();
string sort_merge();
string sort_quick();

int main()
{
cout<<"Ran all tests for\n";
cout<<sort_bubble()<<"\n";
cout<<sort_merge()<<"\n";
cout<<sort_quick()<<"\n";
cout<<"OK\n";
}
Binary file added solutions/Pranjal Rai/src/Assignment 2/test.exe
Binary file not shown.
Binary file added solutions/Pranjal Rai/src/Assignment 2/test.o
Binary file not shown.