Message-oriented data-interchange format.
You can get most of the way to understanding Interface Script if you think of it as being like CSV, but able to support more dimensions.
Example of an Interface Script stream,
# assert the message vectors
i person name age
i org name
# messages
person Jane 34
person Steve 33
org "Piccadilly Steamship Company"
org "Victoria Square Consulting"
The header lines start with 'i'. That is short for 'interface'. It declares the types of message that the sender things is can send.
The receiver should check that this matches its own assumptions.
The remaining lines are messages of those forms.
Interface Assertion address the most common source of bugs seen in message interchange: field mismatch. (This is often caused by version misalignment.)
When there are different assumptions between the sender and receiver, the receiver will fail-fast.
This format forces the receiver to parse away from strings for all fields. For this reason, it should be more robust in practice than interchange formats which have unenforced typing. (e.g. think of a time when you have received NaN in a JSON message, and struggled to work out what was doing on.)
Configuration files.
Files being sent between systems.
Flexible, easy-to-implement network protocols.
If you genuinely need complex tree structures, it's a struggle with Interface Script. In that case, you probably need a document-oriented data interchange format like XML. (Modest nesting is practical in IS - see the heading Nesting below.)
python3 -B -m venv venv
. venv/bin/activate
python3 -B test_is
Write strings. The format is simple enough that it does not need a library.
Here is an example of how you would receive the message format defined above.
Note that we use reflection against the handler. For each message name, you
need a method on_message_name
, with fields matching the document structure.
Example parse code,
import interface_script
DATA = '''
# assert the message vectors
i person name age
i org name
# messages
person Jane 34
person Steve 33
org "Piccadilly Steamship Company"
org "Victoria Square Consulting"
'''
class Handler:
def on_person(self, name, age):
print("Person: %s/%s"%(name, age))
def on_org(self, name):
print("Org: %s"%(name))
def main():
handler = Handler()
ob = interface_script.InterfaceScriptParser(handler)
ob.parse(DATA)
if __name__ == '__main__':
main()
It should be easy to port.
A new consumer would take somewhere between minutes and hours to write. The
line tokeniser is fiddly if you do not have a library for it. But you can find
the logic you need in interface_script/parser.py
.
Windows and unix style will work.
Whitespace is not significant: lines get stripped when they are parsed.
For situations where you needed to send newlines as part of a string, have a convention of exchanging those fields using base64.
The recipient should assume UTF-8 encoding, and this library does. The recipient can reject UTF-8 on a business-logic level if they want to.
Interface Script is not an Interface Definition Language (IDL).
In an IDL, the recipient uses information from the sender to understand the structure of the messages. This format should not be used like that.
Here, the sender is asserting the interface as they understand it. If the receiver disagrees, then it should fail fast.
The author has a background dealing with exchange messaging. A problem that happens in that domain is that you get two thirds of the way through a day, and then a message comes through that you don't understand and things start crashing.
CSV is message-centric. So is Interface Script.
CSV is non-trivial to parse due to quotation mark blocks. A skilled developer could write a parser inside an hour. All this is also true of Interface Script. Their notations are similar.
CSV can describe up to two dimensions (it is tabular). Interface Script can describe an arbitrary number of dimensions.
CSV is weakly defined. Some usages involve a header, others skip the header. Interafce Script is more strongly defined: you should always declare your dimensions.
CSV can use its header for the purpose of interface assertions. Interface Script is more opinionated: you must declare your vectors, and these declarations should be as assertion (and not regarded as an IDL).
JSON can be document-centric or message-centric. Interface Script is message-centric.
JSON parsing is more complex than CSV/IS, but a skilled developer could build a recursive-descent JSON parser within a day.
JSON is more verbose than Interface Script.
JSON lacks interface assertions.
JSON has weak type mechanisms. Interface Script has no type mechanisms.
JSON supports tree structures easily. Interface Script does not.
YAML is document-centric. Interface Script is message-centric.
YAML is harder to parse than JSON, and therefore also harder than IS.
YAML is more verbose than Interface Script.
YAML lacks interface assertions.
YAML supports custom data types. Interface Script has no type mechanisms.
YAML supports tree structures easily. Interface Script does not.
XML is document-centric. Interface Script is message-centric.
XML is complex to parse. Writing a parser to support the full spec might take a single developer a couple of years. People often uses subsets of XML. You could build a parser to handle the basics inside a day.
XML is verbose, moreso than YAML and JSON.
XML lacks interface assertions.
XML schema is more complicated than interface assertions.
XML has a clear encoding strategy.
XML supports tree structures easily. Interface Script does not.
You can create nested data structures by convention. Example,
i bookshelf n
i x_bookshelf
i book title publisher
bookshelf 0
book "Java NIO" "O'Reilly"
book "Graphics Programming in Turbo C" "Wiley"
x_bookshelf
Stick to lower-case characters and the underscore character. Consider that
your handler method will need to have a valid python message name. For
example, on_x_bookshelf
.
If you find your format getting complex, review whether you should be doing all those things in a single communication channel.
Could you instead break the problem up into a few smaller protocols?
Adapted from a personal unreleased project, Orchid (2016).