Browse Source

Python3 update (#8268)

* act fortune fix

* Upgrade toolset to python3

* actions fix

* Update Dockerfile

* Update entrypoint.sh

---------

Co-authored-by: Nate <[email protected]>
ecruz-te 2 years ago
parent
commit
3c447a2452

+ 2 - 2
.github/workflows/build.yml

@@ -48,7 +48,7 @@ jobs:
           echo "PREVIOUS_COMMIT=$(git log --format=%H -n 1 HEAD^2~1)" >> $GITHUB_ENV
           echo "PREVIOUS_COMMIT=$(git log --format=%H -n 1 HEAD^2~1)" >> $GITHUB_ENV
       - uses: actions/setup-python@v4
       - uses: actions/setup-python@v4
         with:
         with:
-          python-version: '2.7'
+          python-version: '3.10'
           architecture: 'x64'
           architecture: 'x64'
       - name: Get all changes vs master
       - name: Get all changes vs master
         env:
         env:
@@ -115,7 +115,7 @@ jobs:
           fetch-depth: 10
           fetch-depth: 10
       - uses: actions/setup-python@v4
       - uses: actions/setup-python@v4
         with:
         with:
-          python-version: '2.7'
+          python-version: '3.10'
           architecture: 'x64'
           architecture: 'x64'
       - name: Get all changes vs master
       - name: Get all changes vs master
         # Runs github_actions_diff, with the the output accessible in later steps
         # Runs github_actions_diff, with the the output accessible in later steps

+ 7 - 12
Dockerfile

@@ -16,29 +16,24 @@ RUN apt-get -yqq update && apt-get -yqq install \
       gcc \
       gcc \
       git-core \
       git-core \
       gosu \
       gosu \
-      libmysqlclient-dev          `# Needed for MySQL-python` \
+      default-libmysqlclient-dev  `# Needed for mysqlclient` \
+      build-essential             `# Needed for mysqlclient` \
       libpq-dev \
       libpq-dev \
-      python2 \
-      python2.7-dev \
+      python3 \
+      python3-dev \
+      python3-pip \
       siege \
       siege \
       software-properties-common
       software-properties-common
 
 
-RUN curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py
-RUN python2 get-pip.py
-
 RUN curl https://raw.githubusercontent.com/paulfitz/mysql-connector-c/master/include/my_config.h --output /usr/include/mysql/my_config.h
 RUN curl https://raw.githubusercontent.com/paulfitz/mysql-connector-c/master/include/my_config.h --output /usr/include/mysql/my_config.h
 
 
 RUN pip install \
 RUN pip install \
       colorama==0.3.1 \
       colorama==0.3.1 \
       docker==4.0.2 \
       docker==4.0.2 \
-      MySQL-python \
+      mysqlclient \
       psutil \
       psutil \
       psycopg2-binary \
       psycopg2-binary \
-      pymongo \
-      requests
-    # Fix for docker-py trying to import one package from the wrong location
-    #cp -r /usr/local/lib/python2.7/dist-packages/backports/ssl_match_hostname \
-    #  /usr/lib/python2.7/dist-packages/backports
+      pymongo
 
 
 ENV FWROOT=/FrameworkBenchmarks PYTHONPATH=/FrameworkBenchmarks
 ENV FWROOT=/FrameworkBenchmarks PYTHONPATH=/FrameworkBenchmarks
 
 

+ 1 - 1
entrypoint.sh

@@ -3,4 +3,4 @@ set -euox pipefail
 
 
 chown -R $USER_ID /var/run/
 chown -R $USER_ID /var/run/
 
 
-gosu $USER_ID python2 /FrameworkBenchmarks/toolset/run-tests.py "$@"
+gosu $USER_ID python3 /FrameworkBenchmarks/toolset/run-tests.py "$@"

+ 3 - 0
toolset/databases/__init__.py

@@ -14,6 +14,9 @@ for folder in db_folders:
     # regex that grabs the characters between "toolset/database/"
     # regex that grabs the characters between "toolset/database/"
     # and the final "/" in the db folder string to get the db name
     # and the final "/" in the db folder string to get the db name
     db_name = re.findall(r'.+\/(.+)\/$', folder, re.M)[0]
     db_name = re.findall(r'.+\/(.+)\/$', folder, re.M)[0]
+    # ignore generate __pycache__ folder
+    if db_name == '__pycache__':
+        continue
     db = imp.load_source("Database", "%s%s.py" % (folder, db_name))
     db = imp.load_source("Database", "%s%s.py" % (folder, db_name))
 
 
     if not hasattr(db.Database, "get_current_world_table")\
     if not hasattr(db.Database, "get_current_world_table")\

+ 15 - 8
toolset/databases/abstract_database.py

@@ -90,15 +90,22 @@ class AbstractDatabase:
         cls.reset_cache(config)
         cls.reset_cache(config)
         #Start siege requests with timeout (20s)
         #Start siege requests with timeout (20s)
         path = config.db_root
         path = config.db_root
-        process = PopenTimeout(shlex.split("siege -c %s -r %s %s -R %s/.siegerc" % (concurrency, count, url, path)), stdout = subprocess.PIPE, stderr = subprocess.STDOUT, timeout=20)
-        output, _ = process.communicate()
-        #Search for failed transactions
-        match = re.search('Failed transactions:.*?(\d+)\n', output, re.MULTILINE)
-        if match:
-            trans_failures = int(match.group(1))
-            print output
+        try:
+            process = subprocess.run(shlex.split(
+                "siege -c %s -r %s %s -R %s/.siegerc" % (concurrency, count, url, path)),
+                stdout = subprocess.PIPE, stderr = subprocess.STDOUT, timeout=20, text=True
+            )  
+        except subprocess.TimeoutExpired as e:
+            print("Verification failed: %s" % (e))
         else:
         else:
-            trans_failures = concurrency * count#Failed transactions: 100%
+            output = process.stdout
+            #Search for failed transactions
+            match = re.search('Failed transactions:.*?(\d+)\n', output, re.MULTILINE)
+            if match:
+                trans_failures = int(match.group(1))
+                print(output)
+            else:
+                trans_failures = concurrency * count #Failed transactions: 100%
 
 
         queries = int(cls.get_queries(config)) - queries
         queries = int(cls.get_queries(config)) - queries
         rows = int(cls.get_rows(config)) - rows
         rows = int(cls.get_rows(config)) - rows

+ 3 - 5
toolset/github_actions/github_actions_diff.py

@@ -94,16 +94,14 @@ run_tests = []
 # Break the test env variable down into test directories
 # Break the test env variable down into test directories
 if os.getenv("TESTLANG"):
 if os.getenv("TESTLANG"):
     dir = "frameworks/" + os.getenv("TESTLANG") + "/"
     dir = "frameworks/" + os.getenv("TESTLANG") + "/"
-    test_dirs = map(lambda x: os.getenv("TESTLANG") + "/" + x,
-                    filter(lambda x: os.path.isdir(dir + x), os.listdir(dir)))
+    test_dirs = [os.getenv("TESTLANG") + "/" + x for x in [x for x in os.listdir(dir) if os.path.isdir(dir + x)]]
+
 elif os.getenv("TESTDIR"):
 elif os.getenv("TESTDIR"):
     test_dirs = os.getenv("TESTDIR").split(' ')
     test_dirs = os.getenv("TESTDIR").split(' ')
 else:
 else:
     def get_frameworks(test_lang):
     def get_frameworks(test_lang):
         dir = "frameworks/" + test_lang + "/"
         dir = "frameworks/" + test_lang + "/"
-        return map(lambda x: test_lang + "/" + x,
-                   filter(lambda x: os.path.isdir(dir + x),
-                          os.listdir(dir)))
+        return [test_lang + "/" + x for x in [x for x in os.listdir(dir) if os.path.isdir(dir + x)]]
     test_dirs = []
     test_dirs = []
     for frameworks in map(get_frameworks, os.listdir("frameworks")):
     for frameworks in map(get_frameworks, os.listdir("frameworks")):
         for framework in frameworks:
         for framework in frameworks:

+ 3 - 0
toolset/test_types/__init__.py

@@ -11,5 +11,8 @@ for folder in test_type_folders:
     # regex that grabs the characters between "toolset/test_types/"
     # regex that grabs the characters between "toolset/test_types/"
     # and the final "/" in the folder string to get the name
     # and the final "/" in the folder string to get the name
     test_type_name = re.findall(r'.+\/(.+)\/$', folder, re.M)[0]
     test_type_name = re.findall(r'.+\/(.+)\/$', folder, re.M)[0]
+    # ignore generated __pycache__ folder
+    if test_type_name == '__pycache__':
+        continue
     test_type = imp.load_source("TestType", "%s%s.py" % (folder, test_type_name))
     test_type = imp.load_source("TestType", "%s%s.py" % (folder, test_type_name))
     test_types[test_type_name] = test_type.TestType
     test_types[test_type_name] = test_type.TestType

+ 1 - 8
toolset/test_types/abstract_test_type.py

@@ -5,7 +5,7 @@ import requests
 from colorama import Fore
 from colorama import Fore
 from toolset.utils.output_helper import log
 from toolset.utils.output_helper import log
 
 
-class AbstractTestType:
+class AbstractTestType(metaclass=abc.ABCMeta):
     '''
     '''
     Interface between a test type (json, query, plaintext, etc) and
     Interface between a test type (json, query, plaintext, etc) and
     the rest of TFB. A test type defines a number of keys it expects
     the rest of TFB. A test type defines a number of keys it expects
@@ -15,7 +15,6 @@ class AbstractTestType:
     passes an argument list of ['spam'], then after parsing there will
     passes an argument list of ['spam'], then after parsing there will
     exist a member `X.spam = 'foobar'`.
     exist a member `X.spam = 'foobar'`.
     '''
     '''
-    __metaclass__ = abc.ABCMeta
 
 
     def __init__(self,
     def __init__(self,
                  config,
                  config,
@@ -40,12 +39,6 @@ class AbstractTestType:
         self.warned = None
         self.warned = None
 
 
     @classmethod
     @classmethod
-    @abc.abstractmethod
-    def url(self):
-        pass
-
-    @classmethod
-    @abc.abstractmethod
     def accept(self, content_type):
     def accept(self, content_type):
         return {
         return {
             'json':
             'json':

+ 2 - 2
toolset/test_types/fortune/fortune.py

@@ -45,8 +45,8 @@ class TestType(AbstractTestType):
             return problems
             return problems
 
 
         parser = FortuneHTMLParser()
         parser = FortuneHTMLParser()
-        parser.feed(body)
-        (valid, diff) = parser.isValidFortune(self.name, body)
+        parser.feed(body.decode())
+        (valid, diff) = parser.isValidFortune(self.name, body.decode())
 
 
         if valid:
         if valid:
             problems += verify_headers(self.request_headers_and_body, headers, url, should_be='html')
             problems += verify_headers(self.request_headers_and_body, headers, url, should_be='html')

+ 2 - 2
toolset/test_types/fortune/fortune_html_parser.py

@@ -1,7 +1,7 @@
 # -*- coding: utf-8
 # -*- coding: utf-8
 import os
 import os
 
 
-from HTMLParser import HTMLParser
+from html.parser import HTMLParser
 from difflib import unified_diff
 from difflib import unified_diff
 
 
 from toolset.utils.output_helper import log
 from toolset.utils.output_helper import log
@@ -9,7 +9,7 @@ from toolset.utils.output_helper import log
 
 
 class FortuneHTMLParser(HTMLParser):
 class FortuneHTMLParser(HTMLParser):
     def __init__(self):
     def __init__(self):
-        HTMLParser.__init__(self)
+        HTMLParser.__init__(self, convert_charrefs=False)
         self.body = []
         self.body = []
 
 
     valid_fortune = '''<!doctype html><html>
     valid_fortune = '''<!doctype html><html>

+ 1 - 1
toolset/test_types/plaintext/plaintext.py

@@ -31,7 +31,7 @@ class TestType(AbstractTestType):
 
 
         # Case insensitive
         # Case insensitive
         body = body.lower()
         body = body.lower()
-        expected = "hello, world!"
+        expected = b"hello, world!"
         extra_bytes = len(body) - len(expected)
         extra_bytes = len(body) - len(expected)
 
 
         if expected not in body:
         if expected not in body:

+ 2 - 2
toolset/test_types/verifications.py

@@ -114,7 +114,7 @@ def verify_helloworld_object(json_object, url):
 
 
     try:
     try:
         # Make everything case insensitive
         # Make everything case insensitive
-        json_object = {k.lower(): v.lower() for k, v in json_object.iteritems()}
+        json_object = {k.lower(): v.lower() for k, v in json_object.items()}
     except:
     except:
         return [('fail', "Not a valid JSON object", url)]
         return [('fail', "Not a valid JSON object", url)]
 
 
@@ -162,7 +162,7 @@ def verify_randomnumber_object(db_object, url, max_infraction='fail'):
                  "Expected a JSON object, got '%s' instead" % got, url)]
                  "Expected a JSON object, got '%s' instead" % got, url)]
 
 
     # Make keys case insensitive
     # Make keys case insensitive
-    db_object = {k.lower(): v for k, v in db_object.iteritems()}
+    db_object = {k.lower(): v for k, v in db_object.items()}
     required_keys = set(['id', 'randomnumber'])
     required_keys = set(['id', 'randomnumber'])
 
 
     for v in (v for v in required_keys if v not in db_object):
     for v in (v for v in required_keys if v not in db_object):

+ 3 - 3
toolset/utils/docker_helper.py

@@ -50,7 +50,7 @@ class DockerHelper:
                 buffer = ""
                 buffer = ""
                 for token in output:
                 for token in output:
                     if 'stream' in token:
                     if 'stream' in token:
-                        buffer += token[token.keys()[0]].encode('utf-8')
+                        buffer += token[list(token.keys())[0]]
                     elif 'errorDetail' in token:
                     elif 'errorDetail' in token:
                         raise Exception(token['errorDetail']['message'])
                         raise Exception(token['errorDetail']['message'])
                     while "\n" in buffer:
                     while "\n" in buffer:
@@ -159,7 +159,7 @@ class DockerHelper:
                             run_log_dir, "%s.log" % docker_file.replace(
                             run_log_dir, "%s.log" % docker_file.replace(
                                 ".dockerfile", "").lower()), 'w') as run_log:
                                 ".dockerfile", "").lower()), 'w') as run_log:
                     for line in docker_container.logs(stream=True):
                     for line in docker_container.logs(stream=True):
-                        log(line, prefix=log_prefix, file=run_log)
+                        log(line.decode(), prefix=log_prefix, file=run_log)
 
 
             extra_hosts = None
             extra_hosts = None
             name = "tfb-server"
             name = "tfb-server"
@@ -400,7 +400,7 @@ class DockerHelper:
         def watch_container(container):
         def watch_container(container):
             with open(raw_file, 'w') as benchmark_file:
             with open(raw_file, 'w') as benchmark_file:
                 for line in container.logs(stream=True):
                 for line in container.logs(stream=True):
-                    log(line, file=benchmark_file)
+                    log(line.decode(), file=benchmark_file)
 
 
         if self.benchmarker.config.network_mode is None:
         if self.benchmarker.config.network_mode is None:
             sysctl = {'net.core.somaxconn': 65535}
             sysctl = {'net.core.somaxconn': 65535}

+ 8 - 8
toolset/utils/metadata.py

@@ -35,9 +35,9 @@ class Metadata:
         '''
         '''
         try:
         try:
             dir = os.path.join(self.benchmarker.config.lang_root, language)
             dir = os.path.join(self.benchmarker.config.lang_root, language)
-            tests = map(lambda x: os.path.join(language, x), os.listdir(dir))
-            return filter(lambda x: os.path.isdir(
-                os.path.join(self.benchmarker.config.lang_root, x)), tests)
+            tests = [os.path.join(language, x) for x in os.listdir(dir)]
+            return [x for x in tests if os.path.isdir(
+                os.path.join(self.benchmarker.config.lang_root, x))]
         except Exception:
         except Exception:
             raise Exception(
             raise Exception(
                 "Unable to locate language directory: {!s}".format(language))
                 "Unable to locate language directory: {!s}".format(language))
@@ -184,7 +184,7 @@ class Metadata:
         # Loop over them and parse each into a FrameworkTest
         # Loop over them and parse each into a FrameworkTest
         for test in config['tests']:
         for test in config['tests']:
 
 
-            tests_to_run = [name for (name, keys) in test.iteritems()]
+            tests_to_run = [name for (name, keys) in test.items()]
 
 
             if "default" not in tests_to_run:
             if "default" not in tests_to_run:
                 log("Framework %s does not define a default test in benchmark_config.json"
                 log("Framework %s does not define a default test in benchmark_config.json"
@@ -193,7 +193,7 @@ class Metadata:
 
 
             # Check that each test configuration is acceptable
             # Check that each test configuration is acceptable
             # Throw exceptions if a field is missing, or how to improve the field
             # Throw exceptions if a field is missing, or how to improve the field
-            for test_name, test_keys in test.iteritems():
+            for test_name, test_keys in test.items():
                 # Validates and normalizes the benchmark_config entry
                 # Validates and normalizes the benchmark_config entry
                 test_keys = Metadata.validate_test(test_name, test_keys,
                 test_keys = Metadata.validate_test(test_name, test_keys,
                                                    config['framework'], directory)
                                                    config['framework'], directory)
@@ -202,7 +202,7 @@ class Metadata:
                 runTests = dict()
                 runTests = dict()
 
 
                 # TODO: remove self.benchmarker.config.types
                 # TODO: remove self.benchmarker.config.types
-                for type_name, type_obj in self.benchmarker.config.types.iteritems():
+                for type_name, type_obj in self.benchmarker.config.types.items():
                     try:
                     try:
                         # Makes a FrameWorkTestType object using some of the keys in config
                         # Makes a FrameWorkTestType object using some of the keys in config
                         # e.g. JsonTestType uses "json_url"
                         # e.g. JsonTestType uses "json_url"
@@ -240,7 +240,7 @@ class Metadata:
         Returns an array suitable for jsonification
         Returns an array suitable for jsonification
         '''
         '''
         all_tests = self.gather_tests()
         all_tests = self.gather_tests()
-        return map(lambda test: {
+        return [{
             "project_name": test.project_name,
             "project_name": test.project_name,
             "name": test.name,
             "name": test.name,
             "approach": test.approach,
             "approach": test.approach,
@@ -257,7 +257,7 @@ class Metadata:
             "notes": test.notes,
             "notes": test.notes,
             "versus": test.versus,
             "versus": test.versus,
             "tags": hasattr(test, "tags") and test.tags or []
             "tags": hasattr(test, "tags") and test.tags or []
-        }, all_tests)
+        } for test in all_tests]
 
 
     def list_test_metadata(self):
     def list_test_metadata(self):
         '''
         '''

+ 10 - 5
toolset/utils/results.py

@@ -17,6 +17,11 @@ from datetime import datetime
 # Cross-platform colored text
 # Cross-platform colored text
 from colorama import Fore, Style
 from colorama import Fore, Style
 
 
+class ByteEncoder(json.JSONEncoder):
+    def default(self, obj):
+        if isinstance(obj, bytes):
+            return obj.decode()
+        return super().default(obj)
 
 
 class Results:
 class Results:
     def __init__(self, benchmarker):
     def __init__(self, benchmarker):
@@ -280,7 +285,7 @@ class Results:
                 log(Fore.CYAN + "| {!s}".format(test.name))
                 log(Fore.CYAN + "| {!s}".format(test.name))
                 if test.name in self.verify.keys():
                 if test.name in self.verify.keys():
                     for test_type, result in self.verify[
                     for test_type, result in self.verify[
-                            test.name].iteritems():
+                            test.name].items():
                         if result.upper() == "PASS":
                         if result.upper() == "PASS":
                             color = Fore.GREEN
                             color = Fore.GREEN
                         elif result.upper() == "WARN":
                         elif result.upper() == "WARN":
@@ -330,7 +335,7 @@ class Results:
     def __write_results(self):
     def __write_results(self):
         try:
         try:
             with open(self.file, 'w') as f:
             with open(self.file, 'w') as f:
-                f.write(json.dumps(self.__to_jsonable(), indent=2))
+                f.write(json.dumps(self.__to_jsonable(), indent=2, cls=ByteEncoder))
         except IOError:
         except IOError:
             log("Error writing results.json")
             log("Error writing results.json")
 
 
@@ -450,10 +455,10 @@ class Results:
         with open(stats_file) as stats:
         with open(stats_file) as stats:
             # dstat doesn't output a completely compliant CSV file - we need to strip the header
             # dstat doesn't output a completely compliant CSV file - we need to strip the header
             for _ in range(4):
             for _ in range(4):
-                stats.next()
+                next(stats)
             stats_reader = csv.reader(stats)
             stats_reader = csv.reader(stats)
-            main_header = stats_reader.next()
-            sub_header = stats_reader.next()
+            main_header = next(stats_reader)
+            sub_header = next(stats_reader)
             time_row = sub_header.index("epoch")
             time_row = sub_header.index("epoch")
             int_counter = 0
             int_counter = 0
             for row in stats_reader:
             for row in stats_reader:

+ 11 - 11
toolset/utils/scaffolding.py

@@ -45,7 +45,7 @@ class Scaffolding:
         self.name = self.display_name.lower()
         self.name = self.display_name.lower()
 
 
     def __prompt_display_name(self):
     def __prompt_display_name(self):
-        self.display_name = raw_input("Name: ").strip()
+        self.display_name = input("Name: ").strip()
 
 
         found = False
         found = False
         for framework in self.benchmarker.metadata.gather_frameworks():
         for framework in self.benchmarker.metadata.gather_frameworks():
@@ -70,7 +70,7 @@ class Scaffolding:
             self.__prompt_language()
             self.__prompt_language()
 
 
     def __prompt_language(self):
     def __prompt_language(self):
-        self.language = raw_input("Language: ").strip()
+        self.language = input("Language: ").strip()
 
 
         known_languages = self.benchmarker.metadata.gather_languages()
         known_languages = self.benchmarker.metadata.gather_languages()
         language = None
         language = None
@@ -107,7 +107,7 @@ class Scaffolding:
         return self.language
         return self.language
 
 
     def __prompt_confirm_new_language(self):
     def __prompt_confirm_new_language(self):
-        self.confirm_new_lang = raw_input("Create New Language '%s' (y/n): " %
+        self.confirm_new_lang = input("Create New Language '%s' (y/n): " %
                                           self.language).strip().lower()
                                           self.language).strip().lower()
         return self.confirm_new_lang == 'y' or self.confirm_new_lang == 'n'
         return self.confirm_new_lang == 'y' or self.confirm_new_lang == 'n'
 
 
@@ -132,7 +132,7 @@ class Scaffolding:
             valid = self.__prompt_approach()
             valid = self.__prompt_approach()
 
 
     def __prompt_approach(self):
     def __prompt_approach(self):
-        self.approach = raw_input("Approach [1/2]: ").strip()
+        self.approach = input("Approach [1/2]: ").strip()
         if self.approach == '1':
         if self.approach == '1':
             self.approach = 'Realistic'
             self.approach = 'Realistic'
         if self.approach == '2':
         if self.approach == '2':
@@ -166,7 +166,7 @@ class Scaffolding:
             self.__gather_platform()
             self.__gather_platform()
 
 
     def __prompt_classification(self):
     def __prompt_classification(self):
-        self.classification = raw_input("Classification [1/2/3]: ").strip()
+        self.classification = input("Classification [1/2/3]: ").strip()
         if self.classification == '1':
         if self.classification == '1':
             self.classification = 'Fullstack'
             self.classification = 'Fullstack'
         if self.classification == '2':
         if self.classification == '2':
@@ -193,7 +193,7 @@ class Scaffolding:
         self.__prompt_platform()
         self.__prompt_platform()
 
 
     def __prompt_platform(self):
     def __prompt_platform(self):
-        self.platform = raw_input("Platform (optional): ").strip()
+        self.platform = input("Platform (optional): ").strip()
         if self.platform == '':
         if self.platform == '':
             self.platform = 'None'
             self.platform = 'None'
 
 
@@ -218,7 +218,7 @@ class Scaffolding:
             valid = self.__prompt_database(prompt, options)
             valid = self.__prompt_database(prompt, options)
 
 
     def __prompt_database(self, prompt, options):
     def __prompt_database(self, prompt, options):
-        self.database = raw_input(prompt).strip()
+        self.database = input(prompt).strip()
         if 0 < int(self.database) <= len(options):
         if 0 < int(self.database) <= len(options):
             self.database = options[int(self.database) - 1]
             self.database = options[int(self.database) - 1]
             return True
             return True
@@ -246,7 +246,7 @@ class Scaffolding:
             valid = self.__prompt_orm()
             valid = self.__prompt_orm()
 
 
     def __prompt_orm(self):
     def __prompt_orm(self):
-        self.orm = raw_input("ORM [1/2/3]: ").strip()
+        self.orm = input("ORM [1/2/3]: ").strip()
         if self.orm == '1':
         if self.orm == '1':
             self.orm = 'Full'
             self.orm = 'Full'
         if self.orm == '2':
         if self.orm == '2':
@@ -269,7 +269,7 @@ class Scaffolding:
         self.__prompt_webserver()
         self.__prompt_webserver()
 
 
     def __prompt_webserver(self):
     def __prompt_webserver(self):
-        self.webserver = raw_input("Webserver (optional): ").strip()
+        self.webserver = input("Webserver (optional): ").strip()
         if self.webserver == '':
         if self.webserver == '':
             self.webserver = 'None'
             self.webserver = 'None'
 
 
@@ -287,7 +287,7 @@ class Scaffolding:
         self.__prompt_versus()
         self.__prompt_versus()
 
 
     def __prompt_versus(self):
     def __prompt_versus(self):
-        self.versus = raw_input("Versus (optional): ").strip()
+        self.versus = input("Versus (optional): ").strip()
         if self.versus == '':
         if self.versus == '':
             self.versus = 'None'
             self.versus = 'None'
 
 
@@ -319,7 +319,7 @@ class Scaffolding:
             print('Aborting')
             print('Aborting')
 
 
     def __prompt_confirmation(self):
     def __prompt_confirmation(self):
-        self.confirmation = raw_input("Initialize [y/n]: ").strip().lower()
+        self.confirmation = input("Initialize [y/n]: ").strip().lower()
         return self.confirmation == 'y' or self.confirmation == 'n'
         return self.confirmation == 'y' or self.confirmation == 'n'
 
 
     def __build_scaffolding(self):
     def __build_scaffolding(self):