Now let’s talk about some (black) technology, hoping to give you some inspiration and help. Now I have a policy file Addition. Policy:

load! : addition_delegate.py await: first_string -> s1 apply: concat_with_time(s1) -> s1 await: second_string -> s2 apply: concat_with_time(s2) -> s2 await: return_result apply: join_with_linefeed(s1, s2) -> result yield: result

There is also a Python source for delegate function addition_delegate. Py:

# addition_delegate.py

def concat_with_time(s):
    import time

    return str(s) + time.ctime()

def join_with_linefeed(s1, s2):
    return "%s\n%s\n" % (str(s1), str(s2))

What kind of grammar is this? But you can pretty much see what it’s trying to do: take two strings one at a time, concatenate them together with the time, and then return the result after you get return_result. And then what? Then we have some Web interfaces, and simply write main.py with web.py:

#! /usr/bin/env python import web from policy import resume class first_str_view: def GET(self): resume("addition.policy", "first_str", value = web.input()["value"], anew = True) return "" class second_str_view: def GET(self): resume("addition.policy", "second_str", value = web.input()["value"]) return "" class return_result_view: def GET(self): return resume("addition.policy", "return_result") urls = [ '/first_str/?', first_str_view, '/second_str/?', second_str_view, '/return_result/?', return_result_view, ] if __name__ == "__main__": app = web.application(urls, globals()) app.run()

If you’ve never used web.py, you’ll probably understand what this structure means, except for resume, but with the addition. Policy above, it seems reasonable. It basically means to disconnect from the acquire and then proceed with the policy after getting input. As you might expect:

9999 $. / main. Py & [1] http://0.0.0.0:9999/ $19121 curl "http://localhost:9999/first_str? value=First+Record+" $ curl "http://localhost:9999/second_str? value=Second+Record+" $ curl "http://localhost:9999/return_result" First Record Sat Sep 5 15:59:25 2015 Second Record Sat Sep 5 15:59:28 2015

This can solve a lot of problems. For example, when the user changes the mailbox, the user first submits the new mailbox, and then has to wait for when he goes to the mailbox to receive the verification mail, so that the operation of changing the mailbox is completed. And then there’s the trickier part of the process, where you have to take a couple of inputs before you can actually put them into the database. For example, you can simply write a policy file and let it control the flow, and the interface only needs to interact with the user:

load! : email_service assert! : is_authenticated await: modify_email -> address apply: send_verf_email(address) await: verf_email_recv apply: save_current_user_info(address)

I have to say that this model is a bit like a coroutine, but it is not implemented in this way, after all: one request completes the whole thread and the whole thread ends, big or small. This is also not WebSocket can solve: such as receiving verification mail, are connected in a second place, where there is Socket to speak of. This is for the case where the time interval between two connections is a disconnect. (Design mode, I think, is a combination of policy + state + interpreter, but I don’t like being constrained by design mode.)

Implementation approach

In the case of Python with exec, it is a good choice to find a way to generate Python code that can be executed with it. Restore execution has these steps:

  • Parse policy file

  • Deserialize the context from the persistent storage device

  • Find out where the breakpoint should be, and follow that location to execute some statements that need to be executed each time (label! ) no.

  • Execute until the next await point and exit

Let’s see what an implementation of resume() might look like:

from policy import policy

import pickle
import os

def resume(pf, await_tag, value = None, anew = False):
    c = context.start_new() if anew else \
        pickle.load(file("context.dump", "rb"))
    p = policy.load(pf)

    p.load_context(c)
    p.provide(await_tag, value)

    ret = None
    
    try:
        ret = p.resume()
    finally:
        os.remove("context.dump")

    if p.is_end():
        os.remove("context.dump")

    return ret

The context and policy here are my implementation of this model, and you can see that they are kept separately, and the policy is almost a constant, hard-coded in a file. The Context is saved every time it exits execution, and is only deleted if the execution has finished or if there was an error.

Policy – Control has on the lot, the code is very short, welcome to view: https://github.com/Shihira/policy-control

Attached: syntax list

digit := '0' | ... | '9' underscore := '_' symbol ::= letter | underscore { letter | underscore | digit } command ::= symbol variable ::= symbol string ::= '"' { [ 0x00 | ... | 0x21 | 0x23 | ... | 0x7f | '\n' | '\r' | '\"' | '\\' ] } '"' value ::= string | variable parameter ::= value parameter-list ::= '(' [ parameter-list ',' parameter | parameter ] ')' argument ::= symbol  [ parameter-list ] argument-list ::= argument-list argument | argument command-line ::= command [ '!' ] ':' argument-list [ '->' variable ] policy ::= policy \n command-line | command-line