-
Notifications
You must be signed in to change notification settings - Fork 21
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
base: master
Are you sure you want to change the base?
Changes from all commits
96c8aa7
bd4aa0d
a06aed8
e7a3dae
a293fac
9b9f646
deb5499
6a7c325
0551e3e
542304e
600ada0
83166bf
4731aee
cafb9c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 |
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() |
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) |
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 | ||
|
||
|
||
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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
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): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This API is suboptimal.
|
||
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() |
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"; | ||
} |
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"; | ||
} |
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"; | ||
} |
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" | ||
} | ||
] |
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) |
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"; | ||
} |
There was a problem hiding this comment.
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.