<commit_before>
import sys
import os
import time
import string
import re
import time
from mimetypes import guess_type
from urllib import quote, unquote
from resources.markup import *

APP_ROOT, file_name = os.path.split(os.path.abspath(__file__))
TESTS = os.path.join(APP_ROOT, "TESTS")
ID_COUNT = os.path.join(APP_ROOT, 'storage', 'ID_COUNT')
IDS = os.path.join(APP_ROOT, 'storage', 'IDS')

if sys.platform == "win32":
    import os, msvcrt
    msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
    msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)

class Entry(object):
    def __init__(self):
        self.title = []
        self.url = []
        self.desc = []
        self.label = []
        self.id = []
        self.buffer = []

def get_ids():
    """Parse the IDS file.

    Parse the IDS file and return a list of the id's.
    Includes all tests and other attributes
    of a protocol like tester and changeset
    to check if a new submitted protocol is complete.
    """
    f_ids = open(IDS, 'r')
    ids = [id.strip() for id in f_ids.readlines()]
    f_ids.close()
    return ids

def get_tests():
    """Parse the TESTS file.

    Parse the TESTS file and return a list of Entry objects
    """
    in_file = open(TESTS, 'r')
    entries = []
    entry = Entry()
    cur = entry.buffer
    for line in in_file.readlines():
        line = line.strip()
        if line.startswith('#'):
            continue
        elif not line:
            entries.append(entry)
            entry = Entry()
            cur = entry.buffer
        elif line.startswith('id:'):
            cur = entry.id
            cur.append(line[3:])
        elif line.startswith('label:'):
            cur = entry.label
            cur.append(line[6:])
        elif line.startswith('desc:'):
            cur = entry.desc
            cur.append(line[5:])
        elif line.startswith('url:'):
            cur = entry.url
            cur.append(line[4:])
        elif line.startswith('***'):
            entry.title = entry.buffer
        else:
            cur.append(line)
    in_file.close()
    return entries

def get_protocol(path):
    """Parse the given protocol.

    Parse the given protocol and return a dictonary with all test ids
    and there 'PASS' 'FAIL' values.
    """
    f = open(path, 'r')
    lines = f.readlines()
    f.close
    return dict((line.strip().split(': ') for line in lines if line.strip()))

def write_tester_changeset(missing, submitted):
    """Create markup for incomplete form submit response
    for the 'tester' and 'cangeset' inputs.
    """
    ret = []
    for key in ["tester", "changeset", "browser"]:
        if key in submitted and submitted[key]:
            ret.append(TR_VALUE_CHECK % (
                "submitted",
                key,
                key,
                VALUE_DISABLED % submitted[key],
                INPUT_HIDDEN % (key, submitted[key])
                )
            )
        else:
            ret.append(TR_VALUE_CHECK % ("missing", key, key, "", ""))

    return "".join(ret)

def write_index_missing(missing, submitted):
    """Create markup for incomplete form submit response with all tests.
    """
    entries = []
    tests = get_tests()
    for entry in tests:
        if entry.title:
            title = "".join(entry.title)
            entries.append(TR_TD_COLS_4 % title)
        if entry.url:
            url = "".join(entry.url)
            entries.append(TR_TD_URL % (url, url))
        if entry.label:
            id = "id-%s" % entry.id[0].strip()
            label = "".join(entry.label)
            desc = "\n    ".join(
                [""] +
                [item.strip(' -*').replace("\"", """)
                            for item in entry.desc if item] +
                [""]
            )
            if id in missing:
                tr = TR_TEST_CHECK % ("missing", desc, label, id, "", id, "", "")
            else:
                tr = TR_TEST_CHECK % ("submitted", desc, label,
                    id,
                    (submitted[id] == "PASS" and " checked" or "") + " disabled",
                    id,
                    (submitted[id] == "FAIL" and " checked" or "") + " disabled",
                    INPUT_HIDDEN % (id, submitted[id])
                )
            entries.append(tr)
    return entries

def write_index():
    """Create markup for a new form submit with all tests.
    """
    entries = []
    for entry in get_tests():
        if entry.title:
            title = "".join(entry.title)
            entries.append(TR_TD_COLS_4 % title)
        if entry.url:
            url = "".join(entry.url)
            entries.append(TR_TD_URL % (url, url))
        if entry.label:
            id = "id-%s" % entry.id[0].strip()
            label = "".join(entry.label)
            raw_items = [item.strip().replace('"', '"') for item in entry.desc if item]
            string = ""
            items = []
            for item in raw_items:
                if item.startswith('-') or item.startswith('*'):
                    if string:
                        items.append(string)
                    string = item.lstrip('-* ')
                else:
                    string += item
            if string:
                items.append(string)
            desc = "\n    ".join([""] + items + [""])
            entries.append(TR_TEST % (desc, label, id, id))
    return entries

def write_protocol(protocol):
    """Create markup for a given protocol with all tests.
    """
    entries = []
    for entry in get_tests():
        if entry.title:
            title = "".join(entry.title)
            entries.append(TR_TD_COLS_4 % title)
        if entry.url:
            url = "".join(entry.url)
            entries.append(TR_TD_URL % (url, url))
        if entry.label:
            id = "id-%s" % entry.id[0].strip()
            label = "".join(entry.label)
            class_name = protocol[id] == "PASS" and "pass" or "fail"
            desc = "\n    ".join(
                [""] +
                [item.strip(' -*').replace("\"", """)
                            for item in entry.desc if item] +
                [""]
            )
            entries.append(TR_TEST_PROTOCOL % (desc, class_name, label, protocol[id]))
    return entries

def write_protocols(protocols):
    """Create markup with a list of all protocols.
    """
    ret = []
    for f_name in protocols:
        ret.append(LIST_LINK % ("./show_protocol/" + f_name, f_name))
    return "".join(ret)

def store_protocol(submitted):
    """Store a complete protocol.

    The file of the stored protocol is <dd>.<mm>.<yy>.<changeset-id>.protocol.
    """
    dd_mm_yy = time.strftime("%d.%m.%y", time.gmtime(time.time()))
    f_name = "%s.%s.%s.protocol" % (dd_mm_yy, submitted["changeset"], submitted["browser"])
    path = os.path.join(APP_ROOT, 'storage', f_name)
    f = open(path, 'w')
    for key in get_ids():
        f.write("%s: %s\n" % (key, submitted[key]))
    f.close()
    return path

def check_test_index():
    """Verify that all tests in the TESTS file have a unique id.
    """
    ID = 1
    LABEL = 2
    DESC = 3
    ERROR = 4
    state = DESC
    in_file = open(TESTS, 'r')
    # Any entry with a label: must be followed by an id: and a desc:
    for line in in_file.readlines():
        if line.startswith('id:'):
            if not state == DESC:
                state = ERROR
                break
            state = ID
        elif line.startswith('label:'):
            if not state == ID:
                state = ERROR
                break
            state = LABEL
        elif line.startswith('desc:'):
            if not state == LABEL:
                state = ERROR
                break
            state = DESC
    in_file.close()
    return not state == ERROR

def add_ids_test_index():
    """Add an id to all tests which ware missing one.
    """
    import shutil
    import tempfile
    ID = 1
    LABEL = 2
    DESC = 3
    ERROR = 4
    state = DESC
    in_file = open(TESTS, 'r')
    f_id_count = open(ID_COUNT, 'r')
    id_count = int(f_id_count.readline().strip())
    f_id_count.close()
    f_ids = open(IDS, 'a')
    tmpfd, tmppath = tempfile.mkstemp(".tmp", "dftests.")
    tmpfile = os.fdopen(tmpfd, "w")
    for line in in_file.readlines():
        if line.startswith('id:'):
            if not state == DESC:
                state = ERROR
                break
            state = ID
        elif line.startswith('label:'):
            if state == DESC:
                tmpfile.write("id: %s\n" % id_count)
                f_ids.write("id-%s\n" % id_count)
                id_count += 1
                state = ID
            if not state == ID:
                state = ERROR
                break
            state = LABEL

        elif line.startswith('desc:'):
            if not state == LABEL:
                state = ERROR
                break
            state = DESC
        tmpfile.write(line)
    tmpfile.close()
    in_file.close()
    f_ids.close()
    if state == ERROR:
        raise AssertionError("Not well formed entry!")
    shutil.copy(tmppath, TESTS)
    os.unlink(tmppath)
    f_id_count = open(ID_COUNT, 'w')
    f_id_count.write(str(id_count))
    f_id_count.close()

def redirect_with_trailing_slash(environ, start_response):
    """Response if the trilling slash is missing
    after the cgi script file name.
    """
    status = "301 Moved Permanently"
    url = "".join([
        environ["wsgi.url_scheme"],
        "://",
        environ["SERVER_NAME"],
        ("SERVER_PORT" in environ and ":" + environ["SERVER_PORT"] or ""),
        environ["REQUEST_URI"],
        "/"])
    start_response(status, [("Location", url)])
    return []

def serve_index(environ, start_response):
    """Serve the entry point of the application.
    """
    status = '200 OK'
    response_headers = [('Content-type', 'text/html')]
    start_response(status, response_headers)
    return [INDEX]

def serve_test_form(environ, start_response):
    """Serve a new test form to submit test results.
    """
    status = '200 OK'
    response_headers = [('Content-type', 'text/html')]
    start_response(status, response_headers)
    if not check_test_index():
        add_ids_test_index()

    script_repo = environ['SCRIPT_NAME']
    doc = [TEST_FORM % (script_repo, script_repo, "", FORM), TR_TESTER_AND_CHANGESET_FORM]
    doc.extend(write_index())
    doc.append(TEST_FORM_END)
    return doc

def serve_protocols(environ, start_response):
    """Serve a list of all protocols.
    """
    status = '200 OK'
    response_headers = [('Content-type', 'text/html')]
    start_response(status, response_headers)
    repo = os.path.join(APP_ROOT, 'storage')
    protocols = [f_name for f_name in os.listdir(repo) if f_name.endswith('.protocol')]

    if not check_test_index():
        add_ids_test_index()
    doc = [PROTOCOL_LIST % write_protocols(protocols)]
    return doc

def serve_protocol(environ, start_response):
    """Serve a stored protocol with all test results of a previous test.
    """
    status = '200 OK'
    response_headers = [('Content-type', 'text/html')]
    start_response(status, response_headers)
    protocol_path = os.path.join(APP_ROOT, 'storage', environ['PATH_INFO'].split("/")[2])
    protocol = get_protocol(protocol_path)
    script_repo = environ['SCRIPT_NAME']
    doc = [
        TEST_FORM % (script_repo, script_repo, "", ""),
        TR_TESTER_AND_CHANGESET % (protocol["tester"], protocol["changeset"], protocol.get("browser", "-"))
    ]
    doc.extend(write_protocol(protocol))
    doc.append(TEST_END)
    return doc

def check_submitted_form(environ, start_response):
    """Check if the submitted test results are complete.
    """
    request_body_size = int(environ['CONTENT_LENGTH'])
    raw_content = environ["wsgi.input"].read(request_body_size)
    submitted = dict([item.split('=') for item in raw_content.split('&')])
    missing = filter(lambda id: not id in submitted or not submitted[id], get_ids())
    status = '200 OK'
    response_headers = [('Content-type', 'text/html')]
    start_response(status, response_headers)
    script_repo = environ['SCRIPT_NAME']

    if missing:
        doc = [
            TEST_FORM % (script_repo, script_repo, LEGEND_MISSING, FORM),
            write_tester_changeset(missing, submitted)
        ]
        doc.extend(write_index_missing(missing, submitted))
        doc.append(TEST_FORM_END)
    else:
        protocol = get_protocol(store_protocol(submitted))
        doc = [
            TEST_FORM % (script_repo, script_repo, "", ""),
            TR_TESTER_AND_CHANGESET % (submitted["tester"], submitted["changeset"], submitted["browser"])
        ]
        doc.extend(write_protocol(protocol))
        doc.append(TEST_END)
    return doc

def serve_static(environ, start_response):
    path_info = environ.get("PATH_INFO", "")
    localpath = os.path.join(APP_ROOT, os.path.normpath(path_info.lstrip('/')))

    if not os.path.isfile(localpath):
        return not_found(environ, start_response)

    mime = guess_type(localpath)[0] or 'text/plain'
    fp = open(localpath, "rb")
    size = os.fstat(fp.fileno())[6]
    response_headers = [
        ('Content-Type', mime),
        ('Content-Length', str(size)),
    ]
    start_response('200 OK', response_headers)
    return [fp.read()]

def not_found(environ, start_response, msg=None):
    status = '404 Not Found'
    response_headers = [('Content-Type', 'text/plain')]
    start_response(status, response_headers)
    return [msg or 'File not found: %s' % environ['PATH_INFO']]

def application(environ, start_response):
    """The main function of the cgi application.
    """
    path_info = environ.get("PATH_INFO", "")
    pos = path_info.find("/", 1)
    handler = pos > -1 and path_info[0:pos] or path_info

    return {
        "": redirect_with_trailing_slash,
        "/": serve_index,
        "/test-form": serve_test_form,
        "/protocols": serve_protocols,
        "/show_protocol": serve_protocol,
        "/resources": serve_static,
        "/submit-form": check_submitted_form,
        "/test-cases": serve_static,
    }.get(handler, not_found)(environ, start_response)

if __name__ == '__main__':
    from wsgiref.simple_server import make_server
    httpd = make_server('', 8002, application)
    print "Serving on port 8002..."
    httpd.serve_forever()
<commit_msg>Added missing check for existence of a test in a given protocol.<commit_after>
import sys
import os
import time
import string
import re
import time
from mimetypes import guess_type
from urllib import quote, unquote
from resources.markup import *

APP_ROOT, file_name = os.path.split(os.path.abspath(__file__))
TESTS = os.path.join(APP_ROOT, "TESTS")
ID_COUNT = os.path.join(APP_ROOT, 'storage', 'ID_COUNT')
IDS = os.path.join(APP_ROOT, 'storage', 'IDS')

if sys.platform == "win32":
    import os, msvcrt
    msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
    msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)

class Entry(object):
    def __init__(self):
        self.title = []
        self.url = []
        self.desc = []
        self.label = []
        self.id = []
        self.buffer = []

def get_ids():
    """Parse the IDS file.

    Parse the IDS file and return a list of the id's.
    Includes all tests and other attributes
    of a protocol like tester and changeset
    to check if a new submitted protocol is complete.
    """
    f_ids = open(IDS, 'r')
    ids = [id.strip() for id in f_ids.readlines()]
    f_ids.close()
    return ids

def get_tests():
    """Parse the TESTS file.

    Parse the TESTS file and return a list of Entry objects
    """
    in_file = open(TESTS, 'r')
    entries = []
    entry = Entry()
    cur = entry.buffer
    for line in in_file.readlines():
        line = line.strip()
        if line.startswith('#'):
            continue
        elif not line:
            entries.append(entry)
            entry = Entry()
            cur = entry.buffer
        elif line.startswith('id:'):
            cur = entry.id
            cur.append(line[3:])
        elif line.startswith('label:'):
            cur = entry.label
            cur.append(line[6:])
        elif line.startswith('desc:'):
            cur = entry.desc
            cur.append(line[5:])
        elif line.startswith('url:'):
            cur = entry.url
            cur.append(line[4:])
        elif line.startswith('***'):
            entry.title = entry.buffer
        else:
            cur.append(line)
    in_file.close()
    return entries

def get_protocol(path):
    """Parse the given protocol.

    Parse the given protocol and return a dictonary with all test ids
    and there 'PASS' 'FAIL' values.
    """
    f = open(path, 'r')
    lines = f.readlines()
    f.close
    return dict((line.strip().split(': ') for line in lines if line.strip()))

def write_tester_changeset(missing, submitted):
    """Create markup for incomplete form submit response
    for the 'tester' and 'cangeset' inputs.
    """
    ret = []
    for key in ["tester", "changeset", "browser"]:
        if key in submitted and submitted[key]:
            ret.append(TR_VALUE_CHECK % (
                "submitted",
                key,
                key,
                VALUE_DISABLED % submitted[key],
                INPUT_HIDDEN % (key, submitted[key])
                )
            )
        else:
            ret.append(TR_VALUE_CHECK % ("missing", key, key, "", ""))

    return "".join(ret)

def write_index_missing(missing, submitted):
    """Create markup for incomplete form submit response with all tests.
    """
    entries = []
    tests = get_tests()
    for entry in tests:
        if entry.title:
            title = "".join(entry.title)
            entries.append(TR_TD_COLS_4 % title)
        if entry.url:
            url = "".join(entry.url)
            entries.append(TR_TD_URL % (url, url))
        if entry.label:
            id = "id-%s" % entry.id[0].strip()
            label = "".join(entry.label)
            desc = "\n    ".join(
                [""] +
                [item.strip(' -*').replace("\"", """)
                            for item in entry.desc if item] +
                [""]
            )
            if id in missing:
                tr = TR_TEST_CHECK % ("missing", desc, label, id, "", id, "", "")
            else:
                tr = TR_TEST_CHECK % ("submitted", desc, label,
                    id,
                    (submitted[id] == "PASS" and " checked" or "") + " disabled",
                    id,
                    (submitted[id] == "FAIL" and " checked" or "") + " disabled",
                    INPUT_HIDDEN % (id, submitted[id])
                )
            entries.append(tr)
    return entries

def write_index():
    """Create markup for a new form submit with all tests.
    """
    entries = []
    for entry in get_tests():
        if entry.title:
            title = "".join(entry.title)
            entries.append(TR_TD_COLS_4 % title)
        if entry.url:
            url = "".join(entry.url)
            entries.append(TR_TD_URL % (url, url))
        if entry.label:
            id = "id-%s" % entry.id[0].strip()
            label = "".join(entry.label)
            raw_items = [item.strip().replace('"', '"') for item in entry.desc if item]
            string = ""
            items = []
            for item in raw_items:
                if item.startswith('-') or item.startswith('*'):
                    if string:
                        items.append(string)
                    string = item.lstrip('-* ')
                else:
                    string += item
            if string:
                items.append(string)
            desc = "\n    ".join([""] + items + [""])
            entries.append(TR_TEST % (desc, label, id, id))
    return entries

def write_protocol(protocol):
    """Create markup for a given protocol with all tests.
    """
    entries = []
    for entry in get_tests():
        if entry.title:
            title = "".join(entry.title)
            entries.append(TR_TD_COLS_4 % title)
        if entry.url:
            url = "".join(entry.url)
            entries.append(TR_TD_URL % (url, url))
        if entry.label:
            id = "id-%s" % entry.id[0].strip()
            if id in protocol:
                label = "".join(entry.label)
                class_name = protocol[id] == "PASS" and "pass" or "fail"
                desc = "\n    ".join(
                    [""] +
                    [item.strip(' -*').replace("\"", """)
                                for item in entry.desc if item] +
                    [""]
                )
                entries.append(TR_TEST_PROTOCOL % (desc, class_name, label, protocol[id]))
    return entries

def write_protocols(protocols):
    """Create markup with a list of all protocols.
    """
    ret = []
    for f_name in protocols:
        ret.append(LIST_LINK % ("./show_protocol/" + f_name, f_name))
    return "".join(ret)

def store_protocol(submitted):
    """Store a complete protocol.

    The file of the stored protocol is <dd>.<mm>.<yy>.<changeset-id>.protocol.
    """
    dd_mm_yy = time.strftime("%d.%m.%y", time.gmtime(time.time()))
    f_name = "%s.%s.%s.protocol" % (dd_mm_yy, submitted["changeset"], submitted["browser"])
    path = os.path.join(APP_ROOT, 'storage', f_name)
    f = open(path, 'w')
    for key in get_ids():
        f.write("%s: %s\n" % (key, submitted[key]))
    f.close()
    return path

def check_test_index():
    """Verify that all tests in the TESTS file have a unique id.
    """
    ID = 1
    LABEL = 2
    DESC = 3
    ERROR = 4
    state = DESC
    in_file = open(TESTS, 'r')
    # Any entry with a label: must be followed by an id: and a desc:
    for line in in_file.readlines():
        if line.startswith('id:'):
            if not state == DESC:
                state = ERROR
                break
            state = ID
        elif line.startswith('label:'):
            if not state == ID:
                state = ERROR
                break
            state = LABEL
        elif line.startswith('desc:'):
            if not state == LABEL:
                state = ERROR
                break
            state = DESC
    in_file.close()
    return not state == ERROR

def add_ids_test_index():
    """Add an id to all tests which ware missing one.
    """
    import shutil
    import tempfile
    ID = 1
    LABEL = 2
    DESC = 3
    ERROR = 4
    state = DESC
    in_file = open(TESTS, 'r')
    f_id_count = open(ID_COUNT, 'r')
    id_count = int(f_id_count.readline().strip())
    f_id_count.close()
    f_ids = open(IDS, 'a')
    tmpfd, tmppath = tempfile.mkstemp(".tmp", "dftests.")
    tmpfile = os.fdopen(tmpfd, "w")
    for line in in_file.readlines():
        if line.startswith('id:'):
            if not state == DESC:
                state = ERROR
                break
            state = ID
        elif line.startswith('label:'):
            if state == DESC:
                tmpfile.write("id: %s\n" % id_count)
                f_ids.write("id-%s\n" % id_count)
                id_count += 1
                state = ID
            if not state == ID:
                state = ERROR
                break
            state = LABEL

        elif line.startswith('desc:'):
            if not state == LABEL:
                state = ERROR
                break
            state = DESC
        tmpfile.write(line)
    tmpfile.close()
    in_file.close()
    f_ids.close()
    if state == ERROR:
        raise AssertionError("Not well formed entry!")
    shutil.copy(tmppath, TESTS)
    os.unlink(tmppath)
    f_id_count = open(ID_COUNT, 'w')
    f_id_count.write(str(id_count))
    f_id_count.close()

def redirect_with_trailing_slash(environ, start_response):
    """Response if the trilling slash is missing
    after the cgi script file name.
    """
    status = "301 Moved Permanently"
    url = "".join([
        environ["wsgi.url_scheme"],
        "://",
        environ["SERVER_NAME"],
        ("SERVER_PORT" in environ and ":" + environ["SERVER_PORT"] or ""),
        environ["REQUEST_URI"],
        "/"])
    start_response(status, [("Location", url)])
    return []

def serve_index(environ, start_response):
    """Serve the entry point of the application.
    """
    status = '200 OK'
    response_headers = [('Content-type', 'text/html')]
    start_response(status, response_headers)
    return [INDEX]

def serve_test_form(environ, start_response):
    """Serve a new test form to submit test results.
    """
    status = '200 OK'
    response_headers = [('Content-type', 'text/html')]
    start_response(status, response_headers)
    if not check_test_index():
        add_ids_test_index()

    script_repo = environ['SCRIPT_NAME']
    doc = [TEST_FORM % (script_repo, script_repo, "", FORM), TR_TESTER_AND_CHANGESET_FORM]
    doc.extend(write_index())
    doc.append(TEST_FORM_END)
    return doc

def serve_protocols(environ, start_response):
    """Serve a list of all protocols.
    """
    status = '200 OK'
    response_headers = [('Content-type', 'text/html')]
    start_response(status, response_headers)
    repo = os.path.join(APP_ROOT, 'storage')
    protocols = [f_name for f_name in os.listdir(repo) if f_name.endswith('.protocol')]

    if not check_test_index():
        add_ids_test_index()
    doc = [PROTOCOL_LIST % write_protocols(protocols)]
    return doc

def serve_protocol(environ, start_response):
    """Serve a stored protocol with all test results of a previous test.
    """
    status = '200 OK'
    response_headers = [('Content-type', 'text/html')]
    start_response(status, response_headers)
    protocol_path = os.path.join(APP_ROOT, 'storage', environ['PATH_INFO'].split("/")[2])
    protocol = get_protocol(protocol_path)
    script_repo = environ['SCRIPT_NAME']
    doc = [
        TEST_FORM % (script_repo, script_repo, "", ""),
        TR_TESTER_AND_CHANGESET % (protocol["tester"], protocol["changeset"], protocol.get("browser", "-"))
    ]
    doc.extend(write_protocol(protocol))
    doc.append(TEST_END)
    return doc

def check_submitted_form(environ, start_response):
    """Check if the submitted test results are complete.
    """
    request_body_size = int(environ['CONTENT_LENGTH'])
    raw_content = environ["wsgi.input"].read(request_body_size)
    submitted = dict([item.split('=') for item in raw_content.split('&')])
    missing = filter(lambda id: not id in submitted or not submitted[id], get_ids())
    status = '200 OK'
    response_headers = [('Content-type', 'text/html')]
    start_response(status, response_headers)
    script_repo = environ['SCRIPT_NAME']

    if missing:
        doc = [
            TEST_FORM % (script_repo, script_repo, LEGEND_MISSING, FORM),
            write_tester_changeset(missing, submitted)
        ]
        doc.extend(write_index_missing(missing, submitted))
        doc.append(TEST_FORM_END)
    else:
        protocol = get_protocol(store_protocol(submitted))
        doc = [
            TEST_FORM % (script_repo, script_repo, "", ""),
            TR_TESTER_AND_CHANGESET % (submitted["tester"], submitted["changeset"], submitted["browser"])
        ]
        doc.extend(write_protocol(protocol))
        doc.append(TEST_END)
    return doc

def serve_static(environ, start_response):
    path_info = environ.get("PATH_INFO", "")
    localpath = os.path.join(APP_ROOT, os.path.normpath(path_info.lstrip('/')))

    if not os.path.isfile(localpath):
        return not_found(environ, start_response)

    mime = guess_type(localpath)[0] or 'text/plain'
    fp = open(localpath, "rb")
    size = os.fstat(fp.fileno())[6]
    response_headers = [
        ('Content-Type', mime),
        ('Content-Length', str(size)),
    ]
    start_response('200 OK', response_headers)
    return [fp.read()]

def not_found(environ, start_response, msg=None):
    status = '404 Not Found'
    response_headers = [('Content-Type', 'text/plain')]
    start_response(status, response_headers)
    return [msg or 'File not found: %s' % environ['PATH_INFO']]

def application(environ, start_response):
    """The main function of the cgi application.
    """
    path_info = environ.get("PATH_INFO", "")
    pos = path_info.find("/", 1)
    handler = pos > -1 and path_info[0:pos] or path_info

    return {
        "": redirect_with_trailing_slash,
        "/": serve_index,
        "/test-form": serve_test_form,
        "/protocols": serve_protocols,
        "/show_protocol": serve_protocol,
        "/resources": serve_static,
        "/submit-form": check_submitted_form,
        "/test-cases": serve_static,
    }.get(handler, not_found)(environ, start_response)

if __name__ == '__main__':
    from wsgiref.simple_server import make_server
    httpd = make_server('', 8002, application)
    print "Serving on port 8002..."
    httpd.serve_forever()
<|endoftext|>


<commit_before>#!/usr/bin/env python
#
# -----------------------------------------------------------------------------
# Copyright (c) 2017 The Regents of the University of California
#
# This file is part of kevlar (http://github.com/standage/kevlar) and is
# licensed under the MIT license: see LICENSE.
# -----------------------------------------------------------------------------

from __future__ import print_function
from collections import defaultdict
import argparse
import itertools
import sys
import networkx
import khmer
import kevlar

def subparser(subparsers):
    subparser = subparsers.add_parser('assemble')
    subparser.add_argument('-d', '--debug', action='store_true',
                           help='print debugging output')
    subparser.add_argument('-o', '--out', metavar='FILE', default=sys.stdout,
                           type=argparse.FileType('w'),
                           help='output file; default is terminal (stdout)')
    subparser.add_argument('--gml', metavar='FILE',
                           help='write graph to .gml file')
    subparser.add_argument('-x', '--max-abund', type=int, metavar='X',
                           default=500, help='discard interesting k-mers that '
                           'occur more than X times')
    subparser.add_argument('augfastq', type=argparse.FileType('r'),
                           help='annotated reads in augmented Fastq format')

def calc_offset(read1, read2, minkmer, debugstream=None):
    maxkmer = kevlar.revcom(minkmer)
    kmer1 = [k for k in read1.ikmers
             if kevlar.same_seq(k.sequence, minkmer, maxkmer)][0]
    kmer2 = [k for k in read2.ikmers
             if kevlar.same_seq(k.sequence, minkmer, maxkmer)][0]
    ksize = len(kmer1.sequence)

    pos1 = kmer1.offset
    pos2 = kmer2.offset
    sameorient = True
    if kmer1.sequence != kmer2.sequence:
        assert kmer1.sequence == kevlar.revcom(kmer2.sequence)
        sameorient = False
        pos2 = len(read2.sequence) - (kmer2.offset + ksize)

    tail, head = read1, read2
    offset = pos1 - pos2
    if pos2 > pos1:
        tail, head = read2, read1
        offset = pos2 - pos1

    segment1 = tail.sequence[offset:]
    headseq = head.sequence if sameorient else kevlar.revcom(head.sequence)
    segment2 = headseq[:(len(headseq)-offset)]
    if segment1 != segment2:
        return None, None, None, None

    if debugstream:
        print('\nDEBUG shared interesting kmer: ', tail.name,
              ' --({:d})({})--> '.format(offset, sameorient), head.name, '\n',
              tail.sequence, '\n', ' ' * pos1, '|' * ksize, '\n', ' ' * offset,
              headseq, '\n', sep='', file=debugstream)

    return tail, head, offset, sameorient

def main(args):
    reads = dict()            # key: read ID, value: record
    kmers = defaultdict(set)  # key: k-mer (min repr), value: set of read IDs
    for n, record in enumerate(kevlar.parse_augmented_fastq(args.augfastq), 1):
        if n % 10000 == 0:
            print('[kevlar::assemble]    loaded {:d} reads'.format(n),
                  file=args.logfile)
        reads[record.name] = record
        for kmer in record.ikmers:
            kmerseq = kevlar.revcommin(kmer.sequence)
            kmers[kmerseq].add(record.name)

    debugout = None
    if args.debug:
        debugout = args.logfile
    graph = networkx.Graph()  # DiGraph?
    nkmers = len(kmers)
    for n, minkmer in enumerate(kmers, 1):
        if n % 100 == 0:
            msg = 'processed {:d}/{:d} shared novel k-mers'.format(n, nkmers)
            print('[kevlar::assemble]    ', msg, sep='', file=args.logfile)
        readnames = kmers[minkmer]
        if len(readnames) > args.max_abund:
            msg = '            skipping k-mer contained by {:d} reads'.format(
                len(readnames)
            )
            print(msg, file=sys.stderr)
            continue
        # print('DDDEBUG', n, len(readnames), file=sys.stderr)
        # continue
        assert len(readnames) > 1
        readset = [reads[rn] for rn in readnames]
        for read1, read2 in itertools.combinations(readset, 2):
            tail, head, offset, sameorient = calc_offset(read1, read2, minkmer,
                                                         debugout)
            if tail is None:  # Shared k-mer but bad overlap
                continue
            if tail in graph and head in graph[tail]:
                assert graph[tail][head]['offset'] == offset
            else:
                graph.add_edge(tail.name, head.name, offset=offset,
                               ikmer=minkmer, orient=sameorient)

    reads_assembled = 0
    for n, cc in enumerate(networkx.connected_components(graph), 1):
        countgraph = khmer.Countgraph(31, 1e6, 4)
        ikmers = set()
        for readname in cc:
            countgraph.consume(record.sequence)
            ikmers.update(set([k.sequence for k in record.ikmers]))
        contigs = set()
        for kmer in ikmers:
            contig = countgraph.assemble_linear_path(kmer)
            contigs.add(contig)
            if len(contigs) > 1:
                unique = set()
                for ctg in sorted(contigs, key=len, reverse=True):
                    ctgrc = kevlar.revcom(ctg)
                    for other in unique:
                        if ctg in other or ctgrc in other:
                            break
                    else:
                        unique.add(ctg)
                contigs = unique
        reads_assembled += len(cc)
        print('CC', n, len(cc), contigs, sep='\t', file=args.out)

    if args.gml:
        networkx.write_gml(graph, args.gml)
        message = '[kevlar::assemble] graph written to {}'.format(args.gml)
        print(message, file=args.logfile)

    message = '[kevlar::assemble] {:d} reads'.format(reads_assembled)
    message += ' assembled into {:d} contigs'.format(n)
    print(message, file=args.logfile)
<commit_msg>A jolly mess [skip ci]<commit_after>#!/usr/bin/env python
#
# -----------------------------------------------------------------------------
# Copyright (c) 2017 The Regents of the University of California
#
# This file is part of kevlar (http://github.com/standage/kevlar) and is
# licensed under the MIT license: see LICENSE.
# -----------------------------------------------------------------------------

from __future__ import print_function
from collections import defaultdict, namedtuple
import argparse
import itertools
import sys
import networkx
import screed
import khmer
import kevlar

SequenceOverlap = namedtuple('SequenceOverlap', 'seq1 seq2 offset sameorient')

def subparser(subparsers):
    subparser = subparsers.add_parser('assemble')
    subparser.add_argument('-d', '--debug', action='store_true',
                           help='print debugging output')
    subparser.add_argument('-o', '--out', metavar='FILE', default=sys.stdout,
                           type=argparse.FileType('w'),
                           help='output file; default is terminal (stdout)')
    subparser.add_argument('--gml', metavar='FILE',
                           help='write graph to .gml file')
    subparser.add_argument('-x', '--max-abund', type=int, metavar='X',
                           default=500, help='discard interesting k-mers that '
                           'occur more than X times')
    subparser.add_argument('augfastq', type=argparse.FileType('r'),
                           help='annotated reads in augmented Fastq format')

def calc_offset(read1, read2, minkmer, debugstream=None):
    maxkmer = kevlar.revcom(minkmer)
    kmer1 = [k for k in read1.ikmers
             if kevlar.same_seq(k.sequence, minkmer, maxkmer)][0]
    kmer2 = [k for k in read2.ikmers
             if kevlar.same_seq(k.sequence, minkmer, maxkmer)][0]
    ksize = len(kmer1.sequence)

    pos1 = kmer1.offset
    pos2 = kmer2.offset
    sameorient = True
    if kmer1.sequence != kmer2.sequence:
        assert kmer1.sequence == kevlar.revcom(kmer2.sequence)
        sameorient = False
        pos2 = len(read2.sequence) - (kmer2.offset + ksize)

    tail, head = read1, read2
    offset = pos1 - pos2
    if pos2 > pos1:
        tail, head = read2, read1
        offset = pos2 - pos1

    segment1 = tail.sequence[offset:]
    headseq = head.sequence if sameorient else kevlar.revcom(head.sequence)
    segment2 = headseq[:(len(headseq)-offset)]
    if segment1 != segment2:
        return None, None, None, None

    if debugstream:
        print('\nDEBUG shared interesting kmer: ', tail.name,
              ' --({:d})({})--> '.format(offset, sameorient), head.name, '\n',
              tail.sequence, '\n', ' ' * pos1, '|' * ksize, '\n', ' ' * offset,
              headseq, '\n', sep='', file=debugstream)

    return tail, head, offset, sameorient

def collapse(seq1, seq2, offset, sameorient):
    if sameorient is False:
        seq2 = kevlar.revcom(seq2)
    firstnewnucl = len(seq2) - (len(seq2) - len(seq1) + offset)
    seq2suffix = seq2[firstnewnucl:]
    return seq1 + seq2suffix

def annotate_contig(record1, record2, newcontig, newname, offset, sameorient):
    assert offset > 1
    newrecord = screed.Record(name=newname, sequence=newcontig,
                              ikmers=record1.ikmers)
    ksize = len(record1.ikmers[0].sequence)
    if sameorient:
        # Next two lines are at high risk for off-by-one errors
        minoffset2keep = len(record2.sequence) - offset - ksize
        keepers = [ik for ik in record2.ikmers if ik.offset > minoffset2keep]
        for k in keepers:
            ikmer = kevlar.KmerOfInterest(k.sequence, k.offset + offset,
                                          k.abund)
            newrecord.ikmers.append(ikmer)
    else:
        maxoffset2keep = offset + ksize
        keepers = [ik for ik in record2.ikmers if ik.offset < maxoffset2keep]
        for k in keepers:
            print()
            ikmer = kevlar.KmerOfInterest(
                kevlar.revcom(k.sequence),
                len(record2.sequence) - k.offset - ksize + offset,
                k.abund,
            )
            newrecord.ikmers.append(ikmer)

    return newrecord

def main(args):
    reads = dict()            # key: read ID, value: record
    kmers = defaultdict(set)  # key: k-mer (min repr), value: set of read IDs
    for n, record in enumerate(kevlar.parse_augmented_fastq(args.augfastq), 1):
        if n % 10000 == 0:
            print('[kevlar::assemble]    loaded {:d} reads'.format(n),
                  file=args.logfile)
        reads[record.name] = record
        for kmer in record.ikmers:
            kmerseq = kevlar.revcommin(kmer.sequence)
            kmers[kmerseq].add(record.name)

    debugout = None
    if args.debug:
        debugout = args.logfile
    graph = networkx.Graph()  # DiGraph?
    nkmers = len(kmers)
    for n, minkmer in enumerate(kmers, 1):
        if n % 100 == 0:
            msg = 'processed {:d}/{:d} shared novel k-mers'.format(n, nkmers)
            print('[kevlar::assemble]    ', msg, sep='', file=args.logfile)
        readnames = kmers[minkmer]
        if len(readnames) > args.max_abund:
            msg = '            skipping k-mer with abundance {:d}'.format(
                len(readnames)
            )
            print(msg, file=sys.stderr)
            continue
        assert len(readnames) > 1
        readset = [reads[rn] for rn in readnames]
        for read1, read2 in itertools.combinations(readset, 2):
            tail, head, offset, sameorient = calc_offset(read1, read2, minkmer,
                                                         debugout)
            if tail is None:  # Shared k-mer but bad overlap
                continue
            if tail.name in graph and head.name in graph[tail.name]:
                assert graph[tail.name][head.name]['offset'] == offset
            else:
                graph.add_edge(tail.name, head.name, offset=offset,
                               ikmer=minkmer, orient=sameorient)

    reads_assembled = 0
    ccs = list(networkx.connected_components(graph))
    for n, cc in enumerate(ccs, 1):
        count = 0
        while len(graph.edges()) > 0:
            count += 1
            edges = sorted(graph.edges(),
                           key=lambda e: graph[e[0]][e[1]]['offset'])
            read1, read2 = edges[0]  # biggest overlap
            seq1 = reads[read1].sequence
            seq2 = reads[read2].sequence
            offset = graph[read1][read2]['offset']
            sameorient = graph[read1][read2]['orient']

            if offset == 0:
                graph.remove_node(read2)
                continue

            newcontig = collapse(seq1, seq2, offset, sameorient)
            newname = 'contig{:d}'.format(count)
            newrecord = annotate_contig(reads[read1], reads[read2], newcontig,
                                        newname, offset, sameorient)
            if debugout:
                print('# DEBUG', read1, read2, offset, sameorient,
                      file=debugout)
                kevlar.print_augmented_fastq(newrecord, debugout)

            for kmer in newrecord.ikmers:
                kmerseq = kevlar.revcommin(kmer.sequence)
                for readname in kmers[kmerseq]:
                    if readname not in graph:
                        continue
                    otherrecord = reads[readname]
                    tail, head, poffset, sameorient = calc_offset(
                        newrecord, otherrecord, kmerseq, debugout
                    )
                    if tail is None:
                        continue
                    if tail.name in graph and head.name in graph[tail.name]:
                        assert graph[tail.name][head.name]['offset'] == poffset
                    else:
                        graph.add_edge(tail.name, head.name, offset=poffset,
                                       ikmer=minkmer, orient=sameorient)

            kmers[kmerseq].add(newrecord.name)
            graph.remove_node(read1)
            graph.remove_node(read2)

        reads_assembled += len(cc)
        print('CC', n, len(cc), sep='\t', file=args.out)

    if args.gml:
        networkx.write_gml(graph, args.gml)
        message = '[kevlar::assemble] graph written to {}'.format(args.gml)
        print(message, file=args.logfile)

    message = '[kevlar::assemble] {:d} reads'.format(reads_assembled)
    message += ' assembled into {:d} contigs'.format(n)
    print(message, file=args.logfile)
<|endoftext|>


<commit_before>'''Python class interface to the keybase.io API.'''

#pylint: disable=R0902

import requests
import os

KEYBASE_BASE_URL = 'https://keybase.io/_/api/'
KEYBASE_API_VERSION = '1.0'

class Keybase(object):

    def __init__(self, username=None):
        '''
        Create a new, empty instance of a Keybase object.

        If you supply a username the user's public information will be
        automatically retrieve. If the username doesn't exist a
        KeybaseUserNotFound exception will be raised.

        If you don't supply a username you can initiate a user lookup by
        using the lookup(username) method on the object after you create
        it.
        '''
        self.__username = None
        self.__lookup_performed = False
        self.__salt = None
        self.__session_cookie = None
        self.__user_object = None
        if username:
            self.lookup(username)

    @property
    def name(self):
        return self._section_getter('profile', 'full_name')

    @property
    def location(self):
        return self._section_getter('profile', 'location')

    @property
    def username(self):
        return self.__username

    @property
    def api_version(self):
        return KEYBASE_API_VERSION

    @property
    def salt(self):
        return self.__salt

    @property
    def session(self):
        return self.__session_cookie

    def _section_getter(self, section, key):
        '''
        Gets a value from a specific section of the user data object.

        Returns the value if the user data object has been loaded, the
        section exists in the user data object and the key exists in
        that section in the user data object:

        >>> k = Keybase('irc')
        >>> k._section_getter('profile', 'full_name')
        u'Ian Chesal'

        Otherwise it returns None if the section doesn't exist:

        >>> if not k._section_getter('invalidsectionname', 'full_name'):
        ...    print 'Section not found!'
        Section not found!

        Or the key doesn't exist in the section:

        >>> if not k._section_getter('profile', 'invalidkeyname'):
        ...    print 'Key not found!'
        Key not found!

        '''
        if self.__user_object:
            if section in self.__user_object:
                if key in self.__user_object[section]:
                    return self.__user_object[section][key]
        return None

    def lookup(self, username):
        '''
        Looks up a user in the keybase.io public directory and initializes
        this Keybase class instance with the user's public keybase.io
        details.

        >>> k = Keybase()
        >>> k.username
        >>> k.lookup('irc')
        >>> k.username
        'irc'

        The lookup() method can be called until the first successful user
        is found in keybase.io. After that, subsequent lookup calls will
        raise a KeybaseLookupInvalidError exception:

        >>> k.lookup('ab')
        Traceback (most recent call last):
        ...
        KeybaseLookupInvalidError: Keybase object already bound to username 'irc'

        To get the private view of the user you need to authenticate as
        the user using the login() method after successfully looking the
        user up in keybase.io.

        If the user cannot be found a KeybaseUserNotFound exception is
        raised:

        >>> k2 = Keybase()
        >>> k2.lookup('abcdefghijklmno123')
        Traceback (most recent call last):
        ...
        KeybaseUserNotFound: User abcdefghijklmno123 not found

        '''
        # If this object is already initialized then the user shouldn't
        # be calling this method a second time.
        if self.__lookup_performed:
            raise KeybaseLookupInvalidError(
                'Keybase object already bound to username \'{}\''.format(self.__username))
        url = KEYBASE_BASE_URL + KEYBASE_API_VERSION + '/user/lookup.json'
        payload = { 'username': username }
        r = requests.get(url, params=payload, timeout=10)
        r.raise_for_status()
        jresponse = r.json()
        if not 'them' in jresponse:
            raise KeybaseUserNotFound('User {} not found'.format(username))
        # Initialize this user from the 'them' part of the reponse.
        self.__user_object = jresponse['them']
        self.__username = username
        self.__lookup_performed = True

    def _get_salt(self):
        '''
        The first round of the two round Keybase login procedure. This
        function gets the salt stored for the user as well as a short-lived
        random challenge string in the form of a login session ID.

        The salt is stored in the object instance's _salt property while
        the login session ID is returned by the function.

        If the object has no email and no username property an KeybaseError
        is thrown. Otherwise the object prefers the username over the
        email if both are set.

        >>> k = Keybase(username='irc')
        >>> print k.salt
        None
        >>> login_session = k._get_salt()
        >>> print k.salt
        5838c199c1b825a069185d5707302693
        '''
        if not self.__username:
            raise KeybaseError('Unable to retrieve salt: no user bound to this class instance')
        url = KEYBASE_BASE_URL + KEYBASE_API_VERSION + '/getsalt.json'
        payload = { 'email_or_username': self.__username }
        r = requests.get(url, params=payload, timeout=10)
        r.raise_for_status()
        jresponse = r.json()
        if not 'salt' in jresponse:
            raise KeybaseError('_get_salt(): No salt value returned for login {0}'.format(login_id))
        if not 'login_session' in jresponse:
            raise KeybaseError('_get_salt(): No login_session value returned for login {0}'.format(login_id))
        self.__salt = jresponse['salt']
        return jresponse['login_session']

    def login(self, password):
        '''
        Executes a two-round login procedure for a user using the supplied
        password to authenticate. The first round involves looking up the
        user and getting their salt and a challenge in the form of a login
        session ID. The second round involves computing the password hash
        and using it to answer the password challenge.

        If the login succeeds the method returns True and a session ID is
        stored in the instance along with all the user object details returned
        by the API when a login is successful.

        If login fails the method throws a KeybaseError with all the details
        for why login failed in the message.
        '''
        pass

class KeybaseError(Exception):
    pass

class KeybaseUserNotFound(Exception):
    '''
    Thrown when calling Keybase.lookup(username) and the username cannot
    be located in the keybase.io public key repository.
    '''
    pass

class KeybaseLookupInvalidError(Exception):
    '''
    Thrown when calling Keybase.lookup(username) on an instance that has
    already been bound to a valid user via another lookup() call.
    '''
    pass

<commit_msg>Pedantic error handling when looking up a user<commit_after>'''Python class interface to the keybase.io API.'''

#pylint: disable=R0902

import requests
import os

KEYBASE_BASE_URL = 'https://keybase.io/_/api/'
KEYBASE_API_VERSION = '1.0'

class Keybase(object):

    def __init__(self, username=None):
        '''
        Create a new, empty instance of a Keybase object.

        If you supply a username the user's public information will be
        automatically retrieve. If the username doesn't exist a
        KeybaseUserNotFound exception will be raised.

        If you don't supply a username you can initiate a user lookup by
        using the lookup(username) method on the object after you create
        it.
        '''
        self.__username = None
        self.__lookup_performed = False
        self.__salt = None
        self.__session_cookie = None
        self.__user_object = None
        if username:
            self.lookup(username)

    @property
    def name(self):
        return self._section_getter('profile', 'full_name')

    @property
    def location(self):
        return self._section_getter('profile', 'location')

    @property
    def username(self):
        return self.__username

    @property
    def api_version(self):
        return KEYBASE_API_VERSION

    @property
    def salt(self):
        return self.__salt

    @property
    def session(self):
        return self.__session_cookie

    def _section_getter(self, section, key):
        '''
        Gets a value from a specific section of the user data object.

        Returns the value if the user data object has been loaded, the
        section exists in the user data object and the key exists in
        that section in the user data object:

        >>> k = Keybase('irc')
        >>> k._section_getter('profile', 'full_name')
        u'Ian Chesal'

        Otherwise it returns None if the section doesn't exist:

        >>> if not k._section_getter('invalidsectionname', 'full_name'):
        ...    print 'Section not found!'
        Section not found!

        Or the key doesn't exist in the section:

        >>> if not k._section_getter('profile', 'invalidkeyname'):
        ...    print 'Key not found!'
        Key not found!

        '''
        if self.__user_object:
            if section in self.__user_object:
                if key in self.__user_object[section]:
                    return self.__user_object[section][key]
        return None

    def lookup(self, username):
        '''
        Looks up a user in the keybase.io public directory and initializes
        this Keybase class instance with the user's public keybase.io
        details.

        >>> k = Keybase()
        >>> k.username
        >>> k.lookup('irc')
        >>> k.username
        'irc'

        The lookup() method can be called until the first successful user
        is found in keybase.io. After that, subsequent lookup calls will
        raise a KeybaseLookupInvalidError exception:

        >>> k.lookup('ab')
        Traceback (most recent call last):
        ...
        KeybaseLookupInvalidError: Keybase object already bound to username 'irc'

        To get the private view of the user you need to authenticate as
        the user using the login() method after successfully looking the
        user up in keybase.io.

        If the user cannot be found a KeybaseUserNotFound exception is
        raised:

        >>> k2 = Keybase()
        >>> k2.lookup('abcdefghijklmno123')
        Traceback (most recent call last):
        ...
        KeybaseUserNotFound: ('User abcdefghijklmno123 not found', {'url': u'https://keybase.io/_/api/1.0/user/lookup.json?username=abcdefghijklmno123', 'desc': u'missing or invalid input'})

        '''
        # If this object is already initialized then the user shouldn't
        # be calling this method a second time.
        if self.__lookup_performed:
            raise KeybaseLookupInvalidError(
                'Keybase object already bound to username \'{}\''.format(self.__username))
        url = KEYBASE_BASE_URL + KEYBASE_API_VERSION + '/user/lookup.json'
        payload = { 'username': username }
        r = requests.get(url, params=payload, timeout=10)
        r.raise_for_status()
        jresponse = r.json()
        # Pendantic searching of the status section of the API's JSON
        # response. We could just leave it up to the 'them' section
        # existing or not but future API changes may require that we
        # handle the response differently based on the statue section
        # in the response and the response codes therein so lets prepare
        # for that now.
        if not 'status' in jresponse or not 'name' in jresponse['status']:
            raise KeybaseError('Malformed API response to user/lookup.json request', {
                'url': r.url,
                'response': r.text
                })
        if jresponse['status']['name'] == 'NOT_FOUND':
            raise KeybaseUserNotFound('User {} not found'.format(username), {
                'url': r.url,
                })
        if jresponse['status']['name'] == 'INPUT_ERROR':
            raise KeybaseUserNotFound('User {} not found'.format(username), {
                'url': r.url,
                'desc': jresponse['status']['desc'],
                })
        if not 'them' in jresponse:
            raise KeybaseError('Malformed API response to user/lookup.json request', {
                'url': r.url,
                'response': r.text
                })
        # Initialize this user from the 'them' part of the reponse.
        self.__user_object = jresponse['them']
        self.__username = username
        self.__lookup_performed = True

    def _get_salt(self):
        '''
        The first round of the two round Keybase login procedure. This
        function gets the salt stored for the user as well as a short-lived
        random challenge string in the form of a login session ID.

        The salt is stored in the object instance's _salt property while
        the login session ID is returned by the function.

        If the object has no email and no username property an KeybaseError
        is thrown. Otherwise the object prefers the username over the
        email if both are set.

        >>> k = Keybase(username='irc')
        >>> print k.salt
        None
        >>> login_session = k._get_salt()
        >>> print k.salt
        5838c199c1b825a069185d5707302693
        '''
        if not self.__username:
            raise KeybaseError('Unable to retrieve salt: no user bound to this class instance')
        url = KEYBASE_BASE_URL + KEYBASE_API_VERSION + '/getsalt.json'
        payload = { 'email_or_username': self.__username }
        r = requests.get(url, params=payload, timeout=10)
        r.raise_for_status()
        jresponse = r.json()
        if not 'salt' in jresponse:
            raise KeybaseError('_get_salt(): No salt value returned for login {0}'.format(login_id))
        if not 'login_session' in jresponse:
            raise KeybaseError('_get_salt(): No login_session value returned for login {0}'.format(login_id))
        self.__salt = jresponse['salt']
        return jresponse['login_session']

    def login(self, password):
        '''
        Executes a two-round login procedure for a user using the supplied
        password to authenticate. The first round involves looking up the
        user and getting their salt and a challenge in the form of a login
        session ID. The second round involves computing the password hash
        and using it to answer the password challenge.

        If the login succeeds the method returns True and a session ID is
        stored in the instance along with all the user object details returned
        by the API when a login is successful.

        If login fails the method throws a KeybaseError with all the details
        for why login failed in the message.
        '''
        pass

class KeybaseError(Exception):
    pass

class KeybaseUserNotFound(Exception):
    '''
    Thrown when calling Keybase.lookup(username) and the username cannot
    be located in the keybase.io public key repository.
    '''
    pass

class KeybaseLookupInvalidError(Exception):
    '''
    Thrown when calling Keybase.lookup(username) on an instance that has
    already been bound to a valid user via another lookup() call.
    '''
    pass

<|endoftext|>


<commit_before>import os
import unittest
import sys
import new
from selenium import webdriver
from sauceclient import SauceClient

browsers = [{
       'platform': "Windows 10",
       'browserName': "chrome",
       'version': "latest",
    }, {
       'platform': "Windows 10",
       'browserName': "MicrosoftEdge",
       'version': "latest",
    }, {
       'platform': "Windows 10",
       'browserName': "firefox",
       'version': "latest",
    }, {
       'platform': "Windows 7",
       'browserName': "internet explorer",
       'version': "latest",
    }, {
       'platform': "OS X 10.11",
       'browserName': "chrome",
       'version': "latest",
    }, {
       'platform': "OS X 10.11",
       'browserName': "safari",
       'version': "latest",
    }, {
       'platform': "OS X 10.10",
       'browserName': "safari",
       'version': "latest",
    }, {
       'platform': "Linux",
       'browserName': "firefox",
       'version': "latest",
    }]

# This decorator is required to iterate over browsers
def on_platforms(platforms):
    def decorator(base_class):
        module = sys.modules[base_class.__module__].__dict__
        for i, platform in enumerate(platforms):
            d = dict(base_class.__dict__)
            d['desired_capabilities'] = platform
            name = "%s_%s" % (base_class.__name__, i + 1)
            module[name] = new.classobj(name, (base_class,), d)

    return decorator

class BaseTest(unittest.TestCase):
    username = os.environ.get('SAUCE_USERNAME')
    access_key = os.environ.get('SAUCE_ACCESS_KEY')
    selenium_port = 'localhost'
    selenium_host = '4445'
    upload = True
    tunnel_id = None
    build_tag = None

    # setUp runs before each test case
    def setUp(self):
        self.desired_capabilities['name'] = self.id()

        if BaseTest.tunnel_id:
            self.desired_capabilities['tunnel-identifier'] = BaseTest.tunnel_id
        if BaseTest.build_tag:
            self.desired_capabilities['build'] = BaseTest.build_tag

        self.driver = webdriver.Remote(
                command_executor="http://%s:%s@%s:%s/wd/hub" %
                                 (BaseTest.username,
                                  BaseTest.access_key,
                                  BaseTest.selenium_host,
                                  BaseTest.selenium_port),
                desired_capabilities=self.desired_capabilities)

    # tearDown runs after each test case
    def tearDown(self):
        self.driver.quit()
        sauce_client = SauceClient(BaseTest.username, BaseTest.access_key)
        status = (sys.exc_info() == (None, None, None))
        sauce_client.jobs.update_job(self.driver.session_id, passed=status)
        test_name = "%s_%s" % (type(self).__name__, self.__name__)
        with(open(test_name + '.testlog', 'w')) as outfile:
            outfile.write("SauceOnDemandSessionID=%s job-name=%s\n" % (self.driver.session_id, test_name))

    @classmethod
    def setup_class(cls):
        cls.build_tag = os.environ.get('BUILD_TAG', None)
        cls.tunnel_id = os.environ.get('TUNNEL_IDENTIFIER', None)
        cls.username = os.environ.get('SAUCE_USERNAME', None)
        cls.access_key = os.environ.get('SAUCE_ACCESS_KEY', None)

        cls.selenium_port = os.environ.get("SELENIUM_PORT", None)
        if cls.selenium_port:
            cls.selenium_host = "localhost"
        else:
            cls.selenium_host = "ondemand.saucelabs.com"
            cls.selenium_port = "80"
<commit_msg>remove edge for testing<commit_after>import os
import unittest
import sys
import new
from selenium import webdriver
from sauceclient import SauceClient

browsers = [{
       'platform': "Windows 10",
       'browserName': "chrome",
       'version': "latest",
    }, {
       'platform': "Windows 10",
       'browserName': "firefox",
       'version': "latest",
    }, {
       'platform': "Windows 7",
       'browserName': "internet explorer",
       'version': "latest",
    }, {
       'platform': "OS X 10.11",
       'browserName': "chrome",
       'version': "latest",
    }, {
       'platform': "OS X 10.11",
       'browserName': "safari",
       'version': "latest",
    }, {
       'platform': "OS X 10.10",
       'browserName': "safari",
       'version': "latest",
    }, {
       'platform': "Linux",
       'browserName': "firefox",
       'version': "latest",
    }]

# This decorator is required to iterate over browsers
def on_platforms(platforms):
    def decorator(base_class):
        module = sys.modules[base_class.__module__].__dict__
        for i, platform in enumerate(platforms):
            d = dict(base_class.__dict__)
            d['desired_capabilities'] = platform
            name = "%s_%s" % (base_class.__name__, i + 1)
            module[name] = new.classobj(name, (base_class,), d)

    return decorator

class BaseTest(unittest.TestCase):
    username = os.environ.get('SAUCE_USERNAME')
    access_key = os.environ.get('SAUCE_ACCESS_KEY')
    selenium_port = 'localhost'
    selenium_host = '4445'
    upload = True
    tunnel_id = None
    build_tag = None

    # setUp runs before each test case
    def setUp(self):
        self.desired_capabilities['name'] = self.id()

        if BaseTest.tunnel_id:
            self.desired_capabilities['tunnel-identifier'] = BaseTest.tunnel_id
        if BaseTest.build_tag:
            self.desired_capabilities['build'] = BaseTest.build_tag

        self.driver = webdriver.Remote(
                command_executor="http://%s:%s@%s:%s/wd/hub" %
                                 (BaseTest.username,
                                  BaseTest.access_key,
                                  BaseTest.selenium_host,
                                  BaseTest.selenium_port),
                desired_capabilities=self.desired_capabilities)

    # tearDown runs after each test case
    def tearDown(self):
        self.driver.quit()
        sauce_client = SauceClient(BaseTest.username, BaseTest.access_key)
        status = (sys.exc_info() == (None, None, None))
        sauce_client.jobs.update_job(self.driver.session_id, passed=status)
        test_name = "%s_%s" % (type(self).__name__, self.__name__)
        with(open(test_name + '.testlog', 'w')) as outfile:
            outfile.write("SauceOnDemandSessionID=%s job-name=%s\n" % (self.driver.session_id, test_name))

    @classmethod
    def setup_class(cls):
        cls.build_tag = os.environ.get('BUILD_TAG', None)
        cls.tunnel_id = os.environ.get('TUNNEL_IDENTIFIER', None)
        cls.username = os.environ.get('SAUCE_USERNAME', None)
        cls.access_key = os.environ.get('SAUCE_ACCESS_KEY', None)

        cls.selenium_port = os.environ.get("SELENIUM_PORT", None)
        if cls.selenium_port:
            cls.selenium_host = "localhost"
        else:
            cls.selenium_host = "ondemand.saucelabs.com"
            cls.selenium_port = "80"
<|endoftext|>


<commit_before># Copyright 2015 Huawei Technologies Co., Ltd.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import mock

from openstack_dashboard.test import helpers

from senlin_dashboard import api
from senlin_dashboard.test.test_data import utils

# Shortcuts to avoid importing openstack_dashboard.test.helper and
# for backwards compatibility.
create_mocks = helpers.create_mocks
IsA = helpers.IsA
IsHttpRequest = helpers.IsHttpRequest

def create_stubs(stubs_to_create={}):
    return helpers.create_stubs(stubs_to_create)

class SenlinTestsMixin(object):
    def _setup_test_data(self):
        super(SenlinTestsMixin, self)._setup_test_data()
        utils.load_test_data(self)

class TestCase(SenlinTestsMixin, helpers.TestCase):
    use_mox = True
    pass

class APITestCase(SenlinTestsMixin, helpers.APITestCase):

    use_mox = True

    def setUp(self):
        super(APITestCase, self).setUp()

        # Store the original senlin client
        self._original_senlinclient = api.senlin.senlinclient

        # Replace the clients with our stubs.
        api.senlin.senlinclient = lambda request: self.stub_senlinclient()

    def tearDown(self):
        super(APITestCase, self).tearDown()
        api.senlin.senlinclient = self._original_senlinclient

    def stub_senlinclient(self):
        if not hasattr(self, "senlinclient"):
            self.senlinclient = mock.Mock()
        return self.senlinclient
<commit_msg>Remove use_mox flag<commit_after># Copyright 2015 Huawei Technologies Co., Ltd.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import mock

from openstack_dashboard.test import helpers

from senlin_dashboard import api
from senlin_dashboard.test.test_data import utils

# Shortcuts to avoid importing openstack_dashboard.test.helper and
# for backwards compatibility.
create_mocks = helpers.create_mocks
IsA = helpers.IsA
IsHttpRequest = helpers.IsHttpRequest

def create_stubs(stubs_to_create={}):
    return helpers.create_stubs(stubs_to_create)

class SenlinTestsMixin(object):
    def _setup_test_data(self):
        super(SenlinTestsMixin, self)._setup_test_data()
        utils.load_test_data(self)

class TestCase(SenlinTestsMixin, helpers.TestCase):
    pass

class APITestCase(SenlinTestsMixin, helpers.APITestCase):

    def setUp(self):
        super(APITestCase, self).setUp()

        # Store the original senlin client
        self._original_senlinclient = api.senlin.senlinclient

        # Replace the clients with our stubs.
        api.senlin.senlinclient = lambda request: self.stub_senlinclient()

    def tearDown(self):
        super(APITestCase, self).tearDown()
        api.senlin.senlinclient = self._original_senlinclient

    def stub_senlinclient(self):
        if not hasattr(self, "senlinclient"):
            self.senlinclient = mock.Mock()
        return self.senlinclient
<|endoftext|>


<commit_before>#!/usr/bin/env python

import serial
import rospy
from sensor_msgs.msg import Temperature,FluidPressure,RelativeHumidity,Illuminance
temp1 = 0;
temp2 = 0;
pressure = 0;
humidity = 0;
light = 0;
def main():
    s = serial.Serial("/dev/launchpad",baudrate=115200,timeout=1)
    temperature_pub = rospy.Publisher('temperature',Temperature,queue_size=10)
    humidity_pub = rospy.Publisher('humidity',RelativeHumidity,queue_size=10)
    pressure_pub = rospy.Publisher('pressure',FluidPressure,queue_size=10)
    light_pub = rospy.Publisher('illuminance',Illuminance,queue_size=10)
    rospy.init_node('sensor_hub',anonymous=True)
    rospy.loginfo("Sensor Hub node started")

    while not rospy.is_shutdown():
        msg = s.readline()
	data = msg.split()
        t = rospy.Time.now()
        try:
            temp1 = float(data[1])
            pressure = float(data[2])
            temp2 = float(data[4])
            humidity = float(data[5])
            light = float(data[7])
            msg = FluidPressure()
            msg.header.stamp = t
            msg.fluid_pressure = pressure
            msg.variance = 0
            pressure_pub.publish(msg)
            msg = Temperature()
            msg.header.stamp = t
            msg.temperature = temp2
            msg.variance = 0
            temperature_pub.publish(msg)
            msg = RelativeHumidity()
            msg.header.stamp = t
            msg.relative_humidity = humidity
            msg.variance = 0
            humidity_pub.publish(msg)
            msg = Illuminance()
            msg.header.stamp = t
            msg.illuminance = light
            msg.variance = 0
            light_pub.publish(msg)
        except rospy.ROSInterruptException:
            raise
        except:
            pass
if __name__ == '__main__':
    try:
        main()
    except rospy.ROSInterruptException:
        pass
<commit_msg>Updated sensor hub package<commit_after>#!/usr/bin/env python

import serial
import rospy
from sensor_msgs.msg import Temperature,FluidPressure,RelativeHumidity,Illuminance
temp1 = 0;
temp2 = 0;
pressure = 0;
humidity = 0;
light = 0;
def main():
    s = serial.Serial("/dev/launchpad",baudrate=115200,timeout=1)
    temperature_pub = rospy.Publisher('temperature',Temperature,queue_size=10)
    humidity_pub = rospy.Publisher('humidity',RelativeHumidity,queue_size=10)
    pressure_pub = rospy.Publisher('pressure',FluidPressure,queue_size=10)
    light_pub = rospy.Publisher('illuminance',Illuminance,queue_size=10)
    rospy.init_node('sensor_hub',anonymous=True)
    rospy.loginfo("Sensor Hub node started")
    s.write('b')

    while not rospy.is_shutdown():
        msg = s.readline()
	data = msg.split()
        t = rospy.Time.now()
        try:
            temp1 = float(data[1])
            pressure = float(data[2])
            temp2 = float(data[4])
            humidity = float(data[5])
            light = float(data[7])
            msg = FluidPressure()
            msg.header.stamp = t
            msg.fluid_pressure = pressure
            msg.variance = 0
            pressure_pub.publish(msg)
            msg = Temperature()
            msg.header.stamp = t
            msg.temperature = temp2
            msg.variance = 0
            temperature_pub.publish(msg)
            msg = RelativeHumidity()
            msg.header.stamp = t
            msg.relative_humidity = humidity
            msg.variance = 0
            humidity_pub.publish(msg)
            msg = Illuminance()
            msg.header.stamp = t
            msg.illuminance = light
            msg.variance = 0
            light_pub.publish(msg)
        except rospy.ROSInterruptException:
            raise
        except:
            s.write('s')
            pass
if __name__ == '__main__':
    try:
        main()
    except rospy.ROSInterruptException:
        pass
<|endoftext|>


<commit_before>import six

from graphite.errors import InputParameterError
from graphite.render.attime import parseTimeOffset
from graphite.logger import log
from graphite.functions.aggfuncs import aggFuncs, aggFuncAliases
from graphite.render.datalib import TimeSeries

class ParamTypes(object):
    pass

class ParamType(object):
    options = []

    def __init__(self, name, validator=None):
        self.name = name
        self.validator = validator

    @classmethod
    def register(cls, name, *args):
        setattr(ParamTypes, name, cls(name, *args))

    def isValid(self, value):
        if self.validator is None:
            # if there's no validator for the type we assume True
            return value

        return self.validator(value)

def validateBoolean(value):
    if isinstance(value, six.string_types):
        if value.lower() == 'true':
            return True
        if value.lower() == 'false':
            return False
        raise ValueError('Invalid boolean value: {value}'.format(value=repr(value)))

    if type(value) in [int, float]:
        if value == 0:
            return False
        if value == 1:
            return True
        raise ValueError('Invalid boolean value: {value}'.format(value=repr(value)))

    return bool(value)

def validateFloat(value):
    return float(value)

def validateInteger(value):
    # prevent that float values get converted to int, because an
    # error is better than silently falsifying the result
    if type(value) is float:
        raise ValueError('Not a valid integer value: {value}'.format(value=repr(value)))

    return int(value)

def validateIntOrInf(value):
    try:
        return validateInteger(value)
    except (TypeError, ValueError):
        pass

    try:
        inf = float('inf')
        if float(value) == inf:
            return inf
    except (TypeError, ValueError, OverflowError):
        pass

    raise ValueError('Not a valid integer nor float value: {value}'.format(value=repr(value)))

def validateInterval(value):
    try:
        parseTimeOffset(value)
    except (IndexError, KeyError, TypeError, ValueError) as e:
        raise ValueError('Invalid interval value: {value}: {e}'.format(value=repr(value), e=str(e)))
    return value

def validateSeriesList(value):
    if not isinstance(value, list):
        raise ValueError('Invalid value type, it is not a list: {value}'.format(value=repr(value)))

    for serie in value:
        if not isinstance(serie, TimeSeries):
            raise ValueError('Invalid type "{type}", should be TimeSeries'.format(type=type(serie)))

    return value

def validateSeriesLists(value):
    if not isinstance(value, list):
        raise ValueError('Invalid value type, it is not a list: {value}'.format(value=repr(value)))

    for entry in value:
        validateSeriesList(entry)

    return value

ParamType.register('boolean', validateBoolean)
ParamType.register('date')
ParamType.register('float', validateFloat)
ParamType.register('integer', validateInteger)
ParamType.register('interval', validateInterval)
ParamType.register('intOrInterval')
ParamType.register('intOrInf', validateIntOrInf)
ParamType.register('node', validateInteger)
ParamType.register('nodeOrTag')
ParamType.register('series')
ParamType.register('seriesList', validateSeriesList)
ParamType.register('seriesLists', validateSeriesLists)
ParamType.register('string')
ParamType.register('tag')

# special type that accepts everything
ParamType.register('any')

class ParamTypeAggFunc(ParamType):

    def __init__(self, name, validator=None):
        if validator is None:
            validator = self.validateAggFuncs

        super(ParamTypeAggFunc, self).__init__(name=name, validator=validator)
        self.options = self.getValidAggFuncs()

    @classmethod
    def getValidAggFuncs(cls):
        return list(aggFuncs.keys()) + list(aggFuncAliases.keys())

    @classmethod
    def getDeprecatedAggFuncs(cls):
        return [name + 'Series' for name in cls.getValidAggFuncs()]

    @classmethod
    def getAllValidAggFuncs(cls):
        return cls.getValidAggFuncs() + cls.getDeprecatedAggFuncs()

    def validateAggFuncs(self, value):
        if value in self.getValidAggFuncs():
            return value

        if value in self.getDeprecatedAggFuncs():
            log.warning('Deprecated aggregation function: {value}'.format(value=repr(value)))
            return value

        raise ValueError('Invalid aggregation function: {value}'.format(value=repr(value)))

ParamTypeAggFunc.register('aggFunc')

class ParamTypeAggOrSeriesFunc(ParamTypeAggFunc):
    options = []

    def __init__(self, name, validator=None):
        if validator is None:
            validator = self.validateAggOrSeriesFuncs
        super(ParamTypeAggOrSeriesFunc, self).__init__(name=name, validator=validator)

    def setSeriesFuncs(self, funcs):
        # check for each of the series functions whether they have an 'aggregator'
        # property being set to 'True'. If so we consider them valid aggregators.
        for name, func in funcs.items():
            if getattr(func, 'aggregator', False) is not True:
                continue

            self.options.append(name)

    def validateAggOrSeriesFuncs(self, value):
        try:
            return self.validateAggFuncs(value)
        except ValueError:
            pass

        if value in self.options:
            return value

        return False

ParamTypeAggOrSeriesFunc.register('aggOrSeriesFunc')

class Param(object):
    __slots__ = ('name', 'type', 'required', 'default', 'multiple', '_options', 'suggestions')

    def __init__(self, name, paramtype, required=False, default=None, multiple=False, options=None, suggestions=None):
        self.name = name
        if not isinstance(paramtype, ParamType):
            raise Exception('Invalid type %s for parameter %s' % (paramtype, name))
        self.type = paramtype
        self.required = bool(required)
        self.default = default
        self.multiple = bool(multiple)
        if options is None:
            options = []
        self._options = options
        self.suggestions = suggestions

    @property
    def options(self):
        options = list(set(getattr(self, '_options', []) + getattr(self.type, 'options', [])))
        options.sort(key=str)
        return options

    def toJSON(self):
        jsonVal = {
            'name': self.name,
            'type': self.type.name,
        }
        if self.required:
            jsonVal['required'] = True
        if self.default is not None:
            jsonVal['default'] = self.default
        if self.multiple:
            jsonVal['multiple'] = True
        if self.options:
            jsonVal['options'] = self.options
        if self.suggestions:
            jsonVal['suggestions'] = self.suggestions
        return jsonVal

    def validateValue(self, value, func):
        # if value isn't specified and there's a default then the default will be used,
        # we don't need to validate the default value because we trust that it is valid
        if value is None and self.default is not None:
            return value

        # None is ok for optional params
        if not self.required and value is None:
            return value

        # parameter is restricted to a defined set of values, but value is not in it
        if self.options and value not in self.options:
            raise InputParameterError(
                'Invalid option specified for function "{func}" parameter "{param}": {value}'.format(
                    func=func, param=self.name, value=repr(value)))

        try:
            return self.type.isValid(value)
        except Exception:
            raise InputParameterError(
                'Invalid "{type}" value specified for function "{func}" parameter "{param}": {value}'.format(
                    type=self.type.name, func=func, param=self.name, value=repr(value)))

def validateParams(func, params, args, kwargs):
    valid_args = []
    valid_kwargs = {}

    # total number of args + kwargs might be larger than number of params if
    # the last param allows to be specified multiple times
    if len(args) + len(kwargs) > len(params):
        if not params[-1].multiple:
            raise InputParameterError(
                'Too many parameters specified for function "{func}"'.format(func=func))

        # if args has more values than params and the last param allows multiple values,
        # then we're going to validate all paramas from the args value list
        args_params = params
        kwargs_params = []
    else:
        # take the first len(args) params and use them to validate the args values,
        # use the remaining params to validate the kwargs values
        args_params = params[:len(args)]
        kwargs_params = params[len(args):]

    # validate the args
    for (i, arg) in enumerate(args):
        if i >= len(args_params):
            # last parameter must be allowing multiple,
            # so we're using it to validate this arg
            param = args_params[-1]
        else:
            param = args_params[i]

        valid_args.append(param.validateValue(arg, func))

    # validate the kwargs
    for param in kwargs_params:
        value = kwargs.get(param.name, None)
        if value is None:
            if param.required:
                raise InputParameterError(
                    'Missing required parameter "{param}" for function "{func}"'
                    .format(param=param.name, func=func))
            continue

        valid_kwargs[param.name] = param.validateValue(value, func)

    if len(kwargs) > len(valid_kwargs):
        unexpected_keys = []
        for name in kwargs.keys():
            if name not in valid_kwargs:
                unexpected_keys.append(name)
        raise InputParameterError(
            'Unexpected key word arguments: {keys}'.format(
                keys=', '.join(
                    key
                    for key in kwargs.keys()
                    if key not in valid_kwargs.keys()
                )))

    return (valid_args, valid_kwargs)
<commit_msg>variable naming<commit_after>import six

from graphite.errors import InputParameterError
from graphite.render.attime import parseTimeOffset
from graphite.logger import log
from graphite.functions.aggfuncs import aggFuncs, aggFuncAliases
from graphite.render.datalib import TimeSeries

class ParamTypes(object):
    pass

class ParamType(object):
    options = []

    def __init__(self, name, validator=None):
        self.name = name
        self.validator = validator

    @classmethod
    def register(cls, name, *args):
        setattr(ParamTypes, name, cls(name, *args))

    def isValid(self, value):
        if self.validator is None:
            # if there's no validator for the type we assume True
            return value

        return self.validator(value)

def validateBoolean(value):
    if isinstance(value, six.string_types):
        if value.lower() == 'true':
            return True
        if value.lower() == 'false':
            return False
        raise ValueError('Invalid boolean value: {value}'.format(value=repr(value)))

    if type(value) in [int, float]:
        if value == 0:
            return False
        if value == 1:
            return True
        raise ValueError('Invalid boolean value: {value}'.format(value=repr(value)))

    return bool(value)

def validateFloat(value):
    return float(value)

def validateInteger(value):
    # prevent that float values get converted to int, because an
    # error is better than silently falsifying the result
    if type(value) is float:
        raise ValueError('Not a valid integer value: {value}'.format(value=repr(value)))

    return int(value)

def validateIntOrInf(value):
    try:
        return validateInteger(value)
    except (TypeError, ValueError):
        pass

    try:
        inf = float('inf')
        if float(value) == inf:
            return inf
    except (TypeError, ValueError, OverflowError):
        pass

    raise ValueError('Not a valid integer nor float value: {value}'.format(value=repr(value)))

def validateInterval(value):
    try:
        parseTimeOffset(value)
    except (IndexError, KeyError, TypeError, ValueError) as e:
        raise ValueError('Invalid interval value: {value}: {e}'.format(value=repr(value), e=str(e)))
    return value

def validateSeriesList(value):
    if not isinstance(value, list):
        raise ValueError('Invalid value type, it is not a list: {value}'.format(value=repr(value)))

    for series in value:
        if not isinstance(series, TimeSeries):
            raise ValueError('Invalid type "{type}", should be TimeSeries'.format(type=type(series)))

    return value

def validateSeriesLists(value):
    if not isinstance(value, list):
        raise ValueError('Invalid value type, it is not a list: {value}'.format(value=repr(value)))

    for entry in value:
        validateSeriesList(entry)

    return value

ParamType.register('boolean', validateBoolean)
ParamType.register('date')
ParamType.register('float', validateFloat)
ParamType.register('integer', validateInteger)
ParamType.register('interval', validateInterval)
ParamType.register('intOrInterval')
ParamType.register('intOrInf', validateIntOrInf)
ParamType.register('node', validateInteger)
ParamType.register('nodeOrTag')
ParamType.register('series')
ParamType.register('seriesList', validateSeriesList)
ParamType.register('seriesLists', validateSeriesLists)
ParamType.register('string')
ParamType.register('tag')

# special type that accepts everything
ParamType.register('any')

class ParamTypeAggFunc(ParamType):

    def __init__(self, name, validator=None):
        if validator is None:
            validator = self.validateAggFuncs

        super(ParamTypeAggFunc, self).__init__(name=name, validator=validator)
        self.options = self.getValidAggFuncs()

    @classmethod
    def getValidAggFuncs(cls):
        return list(aggFuncs.keys()) + list(aggFuncAliases.keys())

    @classmethod
    def getDeprecatedAggFuncs(cls):
        return [name + 'Series' for name in cls.getValidAggFuncs()]

    @classmethod
    def getAllValidAggFuncs(cls):
        return cls.getValidAggFuncs() + cls.getDeprecatedAggFuncs()

    def validateAggFuncs(self, value):
        if value in self.getValidAggFuncs():
            return value

        if value in self.getDeprecatedAggFuncs():
            log.warning('Deprecated aggregation function: {value}'.format(value=repr(value)))
            return value

        raise ValueError('Invalid aggregation function: {value}'.format(value=repr(value)))

ParamTypeAggFunc.register('aggFunc')

class ParamTypeAggOrSeriesFunc(ParamTypeAggFunc):
    options = []

    def __init__(self, name, validator=None):
        if validator is None:
            validator = self.validateAggOrSeriesFuncs
        super(ParamTypeAggOrSeriesFunc, self).__init__(name=name, validator=validator)

    def setSeriesFuncs(self, funcs):
        # check for each of the series functions whether they have an 'aggregator'
        # property being set to 'True'. If so we consider them valid aggregators.
        for name, func in funcs.items():
            if getattr(func, 'aggregator', False) is not True:
                continue

            self.options.append(name)

    def validateAggOrSeriesFuncs(self, value):
        try:
            return self.validateAggFuncs(value)
        except ValueError:
            pass

        if value in self.options:
            return value

        return False

ParamTypeAggOrSeriesFunc.register('aggOrSeriesFunc')

class Param(object):
    __slots__ = ('name', 'type', 'required', 'default', 'multiple', '_options', 'suggestions')

    def __init__(self, name, paramtype, required=False, default=None, multiple=False, options=None, suggestions=None):
        self.name = name
        if not isinstance(paramtype, ParamType):
            raise Exception('Invalid type %s for parameter %s' % (paramtype, name))
        self.type = paramtype
        self.required = bool(required)
        self.default = default
        self.multiple = bool(multiple)
        if options is None:
            options = []
        self._options = options
        self.suggestions = suggestions

    @property
    def options(self):
        options = list(set(getattr(self, '_options', []) + getattr(self.type, 'options', [])))
        options.sort(key=str)
        return options

    def toJSON(self):
        jsonVal = {
            'name': self.name,
            'type': self.type.name,
        }
        if self.required:
            jsonVal['required'] = True
        if self.default is not None:
            jsonVal['default'] = self.default
        if self.multiple:
            jsonVal['multiple'] = True
        if self.options:
            jsonVal['options'] = self.options
        if self.suggestions:
            jsonVal['suggestions'] = self.suggestions
        return jsonVal

    def validateValue(self, value, func):
        # if value isn't specified and there's a default then the default will be used,
        # we don't need to validate the default value because we trust that it is valid
        if value is None and self.default is not None:
            return value

        # None is ok for optional params
        if not self.required and value is None:
            return value

        # parameter is restricted to a defined set of values, but value is not in it
        if self.options and value not in self.options:
            raise InputParameterError(
                'Invalid option specified for function "{func}" parameter "{param}": {value}'.format(
                    func=func, param=self.name, value=repr(value)))

        try:
            return self.type.isValid(value)
        except Exception:
            raise InputParameterError(
                'Invalid "{type}" value specified for function "{func}" parameter "{param}": {value}'.format(
                    type=self.type.name, func=func, param=self.name, value=repr(value)))

def validateParams(func, params, args, kwargs):
    valid_args = []
    valid_kwargs = {}

    # total number of args + kwargs might be larger than number of params if
    # the last param allows to be specified multiple times
    if len(args) + len(kwargs) > len(params):
        if not params[-1].multiple:
            raise InputParameterError(
                'Too many parameters specified for function "{func}"'.format(func=func))

        # if args has more values than params and the last param allows multiple values,
        # then we're going to validate all paramas from the args value list
        args_params = params
        kwargs_params = []
    else:
        # take the first len(args) params and use them to validate the args values,
        # use the remaining params to validate the kwargs values
        args_params = params[:len(args)]
        kwargs_params = params[len(args):]

    # validate the args
    for (i, arg) in enumerate(args):
        if i >= len(args_params):
            # last parameter must be allowing multiple,
            # so we're using it to validate this arg
            param = args_params[-1]
        else:
            param = args_params[i]

        valid_args.append(param.validateValue(arg, func))

    # validate the kwargs
    for param in kwargs_params:
        value = kwargs.get(param.name, None)
        if value is None:
            if param.required:
                raise InputParameterError(
                    'Missing required parameter "{param}" for function "{func}"'
                    .format(param=param.name, func=func))
            continue

        valid_kwargs[param.name] = param.validateValue(value, func)

    if len(kwargs) > len(valid_kwargs):
        unexpected_keys = []
        for name in kwargs.keys():
            if name not in valid_kwargs:
                unexpected_keys.append(name)
        raise InputParameterError(
            'Unexpected key word arguments: {keys}'.format(
                keys=', '.join(
                    key
                    for key in kwargs.keys()
                    if key not in valid_kwargs.keys()
                )))

    return (valid_args, valid_kwargs)
<|endoftext|>