Previous topic

music21.volume

Next topic

music21.webapps.apps

Table Of Contents

Table Of Contents

This Page

music21.webapps

Webapps is a module designed for using music21 with a webserver.

This file includes the classes and functions used to parse and process requests to music21 running on a server.

For information about how to set up a server to use music21, look at the files in webapps.server For examples of application-specific commands and templates, see webapps.apps For details about various output template options available, see webapps.templates

Overview of Processing a Request

1. The GET and POST data from the request are combined into an agenda object. The POST data can be in the formats 'application/json', 'multipart/form-data' or 'application/x-www-form-urlencoded'. For more information, see the documentation for Agenda and makeAgendaFromRequest

2. If an appName is specified, additional data and commands are added to the agenda. For more information, see the applicationInitializers in apps.py.

  1. A CommandProcessor is created for the agenda

4. The processor parses its dataDict into primitives or music21 objects and saves them to a parsedDataDict. For more information, see commandProcessor._parseData()

5. The processor executes its commandList, modifying its internal parsedDataDict. For more information, see executeCommands()

6. If outputTemplate is specified, the processor uses a template to generate and output. For more information, see getOutput() and the templates in templates.py

7. Otherwise, the data will be returned as JSON, where the variables in the agenda’s returnDict specify which variables to include in the returned JSON.

  1. If an error occurs, an error message will be returned to the user

Full JSON Example:

Below is an example of a complete JSON request:

{
    "dataDict": {
        "myNum": {
            "fmt": "int", 
            "data": "23"
        }
    }, 
    "returnDict": {
        "myNum": "int", 
        "ho": "int"
    }, 
    "commandList": [
        {
            "function": "corpus.parse", 
            "argList": [
                "'bwv7.7'"
            ], 
            "resultVar": "sc"
        }, 
        {
            "method": "transpose", 
            "argList": [
                "'p5'"
            ], 
            "caller": "sc", 
            "resultVar": "sc"
        }, 
        {
            "attribute": "flat", 
            "caller": "sc", 
            "resultVar": "scFlat"
        }, 
        {
            "attribute": "highestOffset", 
            "caller": "scFlat", 
            "resultVar": "ho"
        }
    ]
}

Functions

music21.webapps.ModWSGIApplication(environ, start_response)

Application function in proper format for a mod_wsgi Application: Reads the contents of a post request, and passes the data string to webapps.processDataString for further processing.

For an example of how to install this application on a server see music21.webapps.server.wsgiapp.py

The request to the application should have the following structures:

>>> from music21.ext.six import StringIO
>>> environ = {}              # environ is usually created by the server. Manually constructing dictionary for demonstrated
>>> wsgiInput = StringIO()    # wsgi.input is usually a buffer containing the contents of a POST request. Using StringIO to demonstrate
>>> unused = wsgiInput.write('{"dataDict":{"a":{"data":3}},"returnDict":{"a":"int"}}')
>>> unused = wsgiInput.seek(0)
>>> environ['wsgi.input'] = wsgiInput
>>> environ['QUERY_STRING'] = ""
>>> environ['DOCUMENT_ROOT'] = "/Library/WebServer/Documents"
>>> environ['HTTP_HOST'] = "ciconia.mit.edu"
>>> environ['SCRIPT_NAME'] = "/music21/unifiedinterface"
>>> environ['CONTENT_TYPE'] = "application/json"
>>> start_response = lambda status, headers: None         # usually called by mod_wsgi server. Used to initiate response
>>> webapps.ModWSGIApplication(environ, start_response)
[...'{"dataDict": {"a": ...}, "errorList": [], "status": "success"}']    
music21.webapps.makeAgendaFromRequest(requestInput, environ, requestType=None)

Combines information from POST data and server info into an agenda object that can be used with the CommandProcessor.

Takes in a file-like requestInput (has .read()) containing POST data, a dictionary-like environ from the server containing at a minimum a value for the keys QUERY_STRING, and a requestType specifying the content-type of the POST data (‘application/json’,’multipart/form-data’, etc.)

Note that variables specified via query string will be returned as a list if they are specified more than once (e.g. ?b=3&b=4 will yeld ['3', '4'] as the value of b

requestInput should be buffer from the server application. Using StringIO for demonstration

>>> from music21.ext.six import StringIO
>>> requestInput = StringIO()
>>> unused = requestInput.write('{"dataDict":{"a":{"data":3}}}')
>>> unused = requestInput.seek(0)
>>> environ = {"QUERY_STRING":"b=3"}
>>> agenda = webapps.makeAgendaFromRequest(requestInput, environ, 'application/json')
>>> from pprint import pprint as pp
>>> pp(agenda)
{'commandList': [],
 'dataDict': {...'a': {...'data': 3}, 'b': {'data': '3'}},
 'returnDict': {}}

(the ellipses above comment out the u unicode prefix in PY2)

>>> environ2 = {"QUERY_STRING":"a=2&b=3&b=4"}
>>> agenda2 = webapps.makeAgendaFromRequest(requestInput, environ2, 'multipart/form-data')

Note that the 3 in a:data becomes ‘2’ – a string.

>>> pp(agenda2)
{'commandList': [],
 'dataDict': {...'a': {...'data': '2'}, 'b': {'data': ['3', '4']}},
 'returnDict': {}}
music21.webapps.setupApplication(agenda, appName=None)

Given an agenda, determines which application is desired either from the appName parameter or if the appName parameter is none, from the value associated with the “appName” key in the agenda.

If the application name is a valid application name, calls the appropriate application initializer from music21.webapps.apps.py on the agenda.

Agenda

class music21.webapps.Agenda

Subclass of dictionary that represents data and commands to be processed by a CommandProcessor.

The Agenda contains the following keys:

  • ‘dataDict’ whose value is a dictionary specifying data to be input to the processor of the form:

    "dataDict" : {"<VARIABLE_1_NAME>": {"data": "<VARIABLE_1_DATA>",
                                        "fmt":  "<VARIABLE_1_FMT>"},
                  "<VARIABLE_2_NAME>": {"data": "<VARIABLE_2_DATA>",
                                        "fmt":  "<VARIABLE_2_FMT>"},
                  etc.
                  }

    where the variable formats are elements of availableDataFormats (“str”,”int”,”musicxml”, etc.)

  • ‘commandList’ whose value is a list specifying commands to be executed by the processor of the form:

    "commandList" : [{"<CMD_1_TYPE>": "<CMD_2_COMMAND_NAME>",
                      "resultVar":    "<CMD_1_RESULT_VARIABLE>",
                      "caller":       "<CMD_1_CALLER>",
                      "command":      "<CMD_1_COMMAND_NAME>",
                      "argList":      ['<CMD_1_ARG_1>','<CMD_1_ARG_2>'...]},
                      
                      "<CMD_2_TYPE>": "<CMD_2_COMMAND_NAME>",
                      "resultVar":    "<CMD_2_RESULT_VARIABLE>",
                      "caller":       "<CMD_2_CALLER>",
                      "argList":      ['<CMD_2_ARG_1>','<CMD_2_ARG_2>'...]},
                      etc.
                      ]
Calling executeCommands() iterates through the commandList sequentially, calling the equivalent of <CMD_n_RESULT_VARAIBLE> = <CMD_n_CALLER>.<CMD_n_COMMAND_NAME>(<CMD_n_ARG_1>,<CMD_n_ARG_2>...) where the command TYPE is “function”, “method”, or “attribute”
  • ‘returnDict’ whose value is a list specifying the variables to be returned from the server:

    "returnDict" : {"<VARIABLE_1_NAME>": "<VARIABLE_1_FORMAT",
                    "<VARIABLE_2_NAME>": "<VARIABLE_2_FORMAT", etc.}
returnDict is used to limit JSON output to only the relevant variables. If returnDict is not specified, the entire set of variables in the processor’s environment will be returned in string format.
  • ‘outputTemplate’ which specifies the return template to be used
  • ‘outputArgList’ which specifies what arguments to pass the return template

Agenda methods

Agenda.addCommand(commandType, resultVar, caller, command, argList=None)

Adds the specified command to the commandList of the agenda. commandType is either “function”, “attribute” or method. resultVar, caller, and command are strings that will result in the form shown below. Set an argument as none to argList should be a list of data encoded in an appropriate format (see parseInputToPrimitive() for more information)

<resultVar> = <caller>.<command>(<argList>)
>>> from pprint import pprint as pp
>>> agenda = webapps.Agenda()
>>> pp(agenda)
{'commandList': [], 'dataDict': {}, 'returnDict': {}}
>>> agenda.addCommand('method','sc','sc','transpose',['p5'])
>>> pp(agenda)
{'commandList': [{'argList': ['p5'],
                  'caller': 'sc',
                  'method': 'transpose',
                  'resultVar': 'sc'}],
 'dataDict': {},
 'returnDict': {}}
>>> agenda.addCommand('attribute','scFlat','sc','flat')
>>> pp(agenda)
{'commandList': [{'argList': ['p5'],
                  'caller': 'sc',
                  'method': 'transpose',
                  'resultVar': 'sc'},
                 {'attribute': 'flat', 'caller': 'sc', 'resultVar': 'scFlat'}],
 'dataDict': {},
 'returnDict': {}}
Agenda.addData(variableName, data, fmt=None)

Given a variable name, data, and optionally format, constructs the proper dataDictElement structure, and adds it to the dataDict of the agenda.

>>> from pprint import pprint as pp
>>> agenda = webapps.Agenda()
>>> pp(agenda)
{'commandList': [], 'dataDict': {}, 'returnDict': {}}
>>> agenda.addData('a', 2)
>>> pp(agenda)
{'commandList': [], 'dataDict': {'a': {'data': 2}}, 'returnDict': {}}
>>> agenda.addData(variableName='b', data=[1,2,3], fmt='list')
>>> pp(agenda)
{'commandList': [],
 'dataDict': {'a': {'data': 2}, 'b': {'data': [1, 2, 3], 'fmt': 'list'}},
 'returnDict': {}}
Agenda.getData(variableName)

Given a variable name, returns the data stored in the agenda for that variable name. If no data is stored, returns the value None.

>>> from pprint import pprint as pp        
>>> agenda = webapps.Agenda()
>>> pp(agenda)
{'commandList': [], 'dataDict': {}, 'returnDict': {}}
>>> agenda.getData('a') == None
True
>>> agenda.addData('a', 2)
>>> agenda.getData('a')
2
Agenda.loadJson(jsonRequestStr)

Runs json.loads on jsonRequestStr and loads the resulting structure into the agenda object.

>>> from pprint import pprint as pp ## pprint stablizes dictionary order
>>> agenda = webapps.Agenda()
>>> pp(agenda)
{'commandList': [], 'dataDict': {}, 'returnDict': {}}
>>> agenda.loadJson(webapps.sampleJsonStringSimple)
>>> pp(agenda)
{'commandList': [],
 'dataDict': {...'myNum': {...'data': ...'23', ...'fmt': ...'int'}},
 'returnDict': {...'myNum': ...'int'}}
Agenda.setOutputTemplate(outputTemplate, outputArgList)

Specifies the output template that will be used for the agenda.

>>> from pprint import pprint as pp ## pprint stablizes dictionary order
>>> agenda = webapps.Agenda()
>>> pp(agenda)
{'commandList': [], 'dataDict': {}, 'returnDict': {}}
>>> agenda.setOutputTemplate('templates.noteflightEmbed',['sc'])
>>> pp(agenda)
{'commandList': [],
 'dataDict': {},
 'outputArgList': ['sc'],
 'outputTemplate': 'templates.noteflightEmbed',
 'returnDict': {}}

CommandProcessor

class music21.webapps.CommandProcessor(agenda)

Processes server request for music21.

Takes an Agenda (dict) as input, containing the keys:

'dataDict'
'commandList'
'returnDict'
'outputTemplate'
'outputArgList'

CommandProcessor methods

CommandProcessor.executeAttributeCommand(commandElement)

Executes the attribute command specified by commandElement

Function command elements should be dictionaries of the form:

{'attribute': "<ATTRIBUTE_NAME>",
 'caller': "<CALLER_VARIABLE>",
 'resultVar' : "<RESULT_VARIABLE>"}

Executing it yields the equivalent of: <RESULT_VARIABLE> = <CALLER_VARIABLE>.<ATTRIBUTE_NAME>.

All three keys ‘attributeName’, ‘caller’, and ‘resultVar’ are required.

CommandProcessor.executeCommands()

Parses JSON Commands specified in the self.commandList

In the JSON, commands are described by:

‘commandList’ whose value is a list specifying commands to be executed by the processor of the form:

"commandList" : [{"<CMD_1_TYPE>": "<CMD_2_COMMAND_NAME>",
                  "resultVar":    "<CMD_1_RESULT_VARIABLE>",
                  "caller":       "<CMD_1_CALLER>",
                  "command":      "<CMD_1_COMMAND_NAME>",
                  "argList":      ['<CMD_1_ARG_1>','<CMD_1_ARG_2>'...]},
                  
                  "<CMD_2_TYPE>": "<CMD_2_COMMAND_NAME>",
                  "resultVar":    "<CMD_2_RESULT_VARIABLE>",
                  "caller":       "<CMD_2_CALLER>",
                  "argList":      ['<CMD_2_ARG_1>','<CMD_2_ARG_2>'...]},
                  etc.
                  ]

Calling .executeCommands() iterates through the commandList sequentially, calling the equivalent of:

<CMD_n_RESULT_VARAIBLE> = <CMD_n_CALLER>.<CMD_n_COMMAND_NAME>(<CMD_n_ARG_1>,<CMD_n_ARG_2>...)

where the command TYPE is “function” (no caller), “method” (has a caller), or “attribute”

See executeFunctionCommand(), executeMethodCommand(), and executeAttributeCommand() for more information about the format required for those commands.

EXAMPLE:

{"commandList:"[
    {"function":"corpus.parse",
     "argList":["'bwv7.7'"],
     "resultVar":"sc"},
     
    {"method":"transpose",
     "caller":"sc",
     "argList":["'p5'"],
     "resultVar":"sc"},
     
    
    {"attribute":"flat",
     "caller":"sc",
     "resultVar":"scFlat"},
     
    {"attribute":"higestOffset",
     "caller":"scFlat",
     "resultVar":"ho"}
     ]
}
CommandProcessor.executeFunctionCommand(commandElement)

Executes the function command specified by commandElement.

Function command elements should be dictionaries of the form:

{'function': "<FUNCTION_NAME>",
 'argList': ["<ARG_1>","<ARG_2>", etc.],
 'resultVar' : "<RESULT_VARIABLE>"}

Executing it yields the equivalent of: <RESULT_VARIABLE> = <FUNCTION_NAME>(ARG_1, ARG_2, ...)

The keys argList and resultVar are optional. A commandElement without argList will just call <FUNCTION_NAME>() with no arguments and a commandElement without resutlVar will not assign the result of the function to any variable.

CommandProcessor.executeMethodCommand(commandElement)

Example:

{'method': "<METHOD_NAME>",
 'caller': "<CALLER_VARIABLE>",
 'argList': ["<ARG_1>","<ARG_2>", etc.],
 'resultVar' : "<RESULT_VARIABLE>"}

Executing it yields the equivalent of <RESULT_VARIABLE> = <CALLER_VARIABLE>.<METHOD_NAME>(ARG_1, ARG_2, ...)

The keys argList and resultVar are optional. A commandElement without argList will just call <CALLER_VARIABLE>.<METHOD_NAME>() with no arguments and a commandElement without resutlVar will not assign the result of the function to any variable.

CommandProcessor.getErrorStr()

Converts self.errorList into a string

CommandProcessor.getOutput()

Generates the output of the processor. Uses the attributes outputTemplate and outputArgList from the agenda to determine which format the output should be in. If an outputTemplate is unspecified or known, will return json by default.

Return is of the style (output, outputType) where outputType is a content-type ready for returning to the server: “text/plain”, “application/json”, “text/html”, etc.

CommandProcessor.getResultObject()

Returns a new object ready for json parsing with the string values of the objects specified in self.returnDict in the formats specified in self.returnDict:

"returnDict":{
    "myNum" : "int",
    "ho"    : "int"
}
CommandProcessor.parseInputToPrimitive(inpVal)

Determines what format a given input is in and returns a value in that format.. First checks if it is the name of a variable defined in the parsedDataDict or the name of an allowable function. In either of these cases, it will return the actual value of the data or the actual function.

Next, it will check if the string is an int, float, boolean, or none, returning the appropriate value. If it is a quoted string then it will remove the quotes on the ends and return it as a string. If it has square braces indicating a list, the inner elements will be parsed using this same function recursively. (Note that recursive lists like [1, 2, [3, 4]] are not yet supported

If the input corresponds to none of these types, it is returned as a string.

>>> agenda = webapps.Agenda()
>>> agenda.addData("a",2)
>>> agenda.addData("b",[1,2,3],"list")
>>> processor = webapps.CommandProcessor(agenda)
>>> processor.parseInputToPrimitive("a")
2
>>> processor.parseInputToPrimitive("b")
[1, 2, 3]
>>> processor.parseInputToPrimitive("1.0")
1.0
>>> processor.parseInputToPrimitive("2")
2
>>> processor.parseInputToPrimitive("True")
True
>>> processor.parseInputToPrimitive("False")
False
>>> processor.parseInputToPrimitive("None") == None
True
>>> processor.parseInputToPrimitive("'hi'")
'hi'
>>> processor.parseInputToPrimitive("'Madam I'm Adam'")
"Madam I'm Adam"
>>> processor.parseInputToPrimitive("[1,2,3]")
[1, 2, 3]
>>> processor.parseInputToPrimitive("[1,'hi',3.0,True, a, justAStr]")
[1, 'hi', 3.0, True, 2, 'justAStr']
CommandProcessor.recordError(errorString, exceptionObj=None)

Adds an error to the internal errorList array and prints the whole error to stderr so both the user and the administrator know. Error string represents a brief, human-readable message decribing the error.

Errors are appended to the errorList as a tuple (errorString, errorTraceback) where errorTraceback is the traceback of the exception if exceptionObj is specified, otherwise errorTraceback is the empty string