• 修改jinja2模板中变量标识符

    使用webpy时,修改jinja2变量标识

    重写webpy包中的template.py文件中的render_jinja类.

    class my_render_jinja:
        """Rendering interface to Jinja2 Templates
    
        Example:
    
            render= render_jinja('templates')
            render.hello(name='jinja2')
        """
        def __init__(self, *a, **kwargs):
            extensions = kwargs.pop('extensions', [])
            globals = kwargs.pop('globals', \{\})
    
            from jinja2 import Environment, FileSystemLoader
            self._lookup = Environment(loader=FileSystemLoader(*a, **kwargs), extensions=extensions)
            # 添加下面两句代码,修改标识符
            self._lookup.variable_start_string = '\{\{ '
            self._lookup.variable_end_string = ' \}\}'
            self._lookup.globals.update(globals)
    
        def __getattr__(self, name):
            # Assuming all templates end with .html
            path = name + '.html'
            t = self._lookup.get_template(path)
            return t.render
    

    有其它方式吗?

    jinja2的变量标识符可以通过以下方式访问到,但是无法被修改

    # 可以通过以下方式访问
    import jinja2
    jinja2.defaults.VARIABLE_START_STRING
    jinja2.defaults.VARIABLE_END_STRING
    jinja2.environment.VARIABLE_START_STRING
    jinja2.environment.VARIABLE_END_STRING
    
    # 使用以下方式赋值均无效
    jinja2.defaults.VARIABLE_START_STRING = '\{\{ '
    jinja2.defaults.VARIABLE_END_STRING = ' \}\}'
    jinja2.environment.VARIABLE_START_STRING = '\{\{ '
    jinja2.environment.VARIABLE_END_STRING = ' \}\}'
    

    为什么无效?

    原因是导入jinja2时,执行了jinja2包中的init.py文件,而init.py中导入Environment对象
    Environment对象的构造函数init(variable_start_string=VARIABLE_START_STRING,variable_end_string=VARIABLE_END_STRING,...)有默认值
    默认值在Environment被导入时,已经固定,之后无法被修改
    所以使用赋值的方式无效

    了解原因后,可以找到另外一种方法,在import前设置

    __docformat__ = 'restructuredtext en'
    __version__ = '2.7.2'
    
    # high level interface
    # 在此处添加下面三句代码
    import defaults
    defaults.VARIABLE_START_STRING = '\{\{ '
    defaults.VARIABLE_END_STRING = ' \}\}'
    from jinja2.environment import Environment, Template
    
    # loaders
    from jinja2.loaders import BaseLoader, FileSystemLoader, PackageLoader, \
         DictLoader, FunctionLoader, PrefixLoader, ChoiceLoader, \
         ModuleLoader
    ```python
    
  • angularjs过滤器(number)

    用来精确浮点数(指定小数点位数).

    在html中用法

    
    

    在js中用法

    $filter('number')(number, fractionSize)
    

    参数

    • number 待精确的数字
    • factionSize(可选) 小数点后精确位数,默认值是3.(默认情况下保留的小数位数小于等于3. 比如: 1234-->1234;1234.56789-->1234.568;1234.56-->1234.56)
      ##例子
    <!doctype html>
    <html ng-app='demo'>
    <meta charset='utf-8'>
    <body>
    <div ng-controller="ExampleController">
      输入数字: <input ng-model='val'><br>
      <!-- 默认格式 -->
      默认格式: <span id='number-default'></span><br>
      <!-- factionSize=0 -->
      保留0位: <span></span><br>
      <!--factionSize大于小数点位数 -->
      保留10位: <span></span><br>
      <!-- factionSize小于小数点位数-->
      保留2位: <span></span>
    </div>
    
    <script src="/static/lib/angular/angular.js"></script>
    <script src="/static/lib/angular-resource/angular-resource.min.js"></script>
    <script>
        var app = angular.module('demo', ['ngResource'])
        .controller('ExampleController', function($scope) {
            $scope.val = 1234.56789;
        });
    </script>
    </body>
    </html>
    

    显示结果

    输入数字: 1,234.56789
    默认格式: 1,234.568
    保留0位: 1,235
    保留10位: 1,234.5678900000
    保留2位: 1,234.57
    
  • 判断端口通不通的几种方法

    通常使用"telnet ip port"判断端口通不通. 有其它方法吗?先看下面的几种方法

    准备环境

    启动一个web服务器,提供端口.

    [wyq@localhost ~]$ python -m SimpleHTTPServer 8080
    Serving HTTP on 0.0.0.0 port 8080 ...
    

    用其它web服务器提供端口也一样,由于python比较方便,这里就用它

    用telnet判断

    telnet是windows标准服务,可以直接用;如果是linux机器,需要安装telnet.

    用法: telnet ip port

    先用telnet连接不存在的端口

    [wyq@localhost ~]$ telnet localhost 9000
    Trying 127.0.0.1...
    telnet: connect to address 127.0.0.1: Connection refused
    

    直接提示连接被拒绝

    再连接上面提供的端口

    [wyq@localhost ~]$ telnet localhost 8080
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.
    

    看到"Connected to localhost." 可以知道连接成功了.

    后台服务器有什么反映?

    [wyq@localhost monitor]$ python -m SimpleHTTPServer 8080
    Serving HTTP on 0.0.0.0 port 8080 ...
    

    没有任何反映

    此时telnet停住了,随便输入一个字符"a",然后回车

    [wyq@localhost ~]$ telnet localhost 8080
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.
    a
    <head>
    <title>Error response</title>
    </head>
    <body>
    <h1>Error response</h1>
    <p>Error code 400.
    <p>Message: Bad request syntax ('a').
    <p>Error code explanation: 400 = Bad request syntax or unsupported method.
    </body>
    Connection closed by foreign host.
    

    再看服务器

    [wyq@localhost monitor]$ python -m SimpleHTTPServer 8080
    Serving HTTP on 0.0.0.0 port 8080 ...
    127.0.0.1 - - [22/Aug/2014 07:15:16] code 400, message Bad request syntax ('a')
    127.0.0.1 - - [22/Aug/2014 07:15:16] "a" 400 -
    

    上面是linux环境下telnet连接一个web服务端口的情况.

    用ssh判断

    ssh是linux的标准配置并且最常用,可以用来判断端口吗?

    用法: ssh -v -p port username@ip
    -v 调试模式(会打印日志).
    -p 指定端口
    usernmae可以随意

    连接不存在端口

    [wyq@localhost ~]$ ssh -v -p 9000 wyq@localhost
    OpenSSH_6.4, OpenSSL 1.0.1e-fips 11 Feb 2013
    debug1: Reading configuration data /home/wyq/.ssh/config
    debug1: Reading configuration data /etc/ssh/ssh_config
    debug1: /etc/ssh/ssh_config line 51: Applying options for *
    debug1: Connecting to localhost [127.0.0.1] port 9000.
    debug1: connect to address 127.0.0.1 port 9000: Connection refused
    ssh: connect to host localhost port 9000: Connection refused
    

    "Connection refused"表示端口不可用

    连接存在的端口

    [wyq@localhost ~]$ ssh -v -p 8080 wyq@localhost
    OpenSSH_6.4, OpenSSL 1.0.1e-fips 11 Feb 2013
    debug1: Reading configuration data /home/wyq/.ssh/config
    debug1: Reading configuration data /etc/ssh/ssh_config
    debug1: /etc/ssh/ssh_config line 51: Applying options for *
    debug1: Connecting to localhost [127.0.0.1] port 8080.
    debug1: Connection established.
    debug1: identity file /home/wyq/.ssh/id_rsa type 1
    debug1: identity file /home/wyq/.ssh/id_rsa-cert type -1
    debug1: identity file /home/wyq/.ssh/id_dsa type -1
    debug1: identity file /home/wyq/.ssh/id_dsa-cert type -1
    debug1: identity file /home/wyq/.ssh/id_ecdsa type -1
    debug1: identity file /home/wyq/.ssh/id_ecdsa-cert type -1
    debug1: Enabling compatibility mode for protocol 2.0
    debug1: Local version string SSH-2.0-OpenSSH_6.4
    debug1: ssh_exchange_identification: <head>
    
    debug1: ssh_exchange_identification: <title>Error response</title>
    
    debug1: ssh_exchange_identification: </head>
    
    debug1: ssh_exchange_identification: <body>
    
    debug1: ssh_exchange_identification: <h1>Error response</h1>
    
    debug1: ssh_exchange_identification: <p>Error code 400.
    
    debug1: ssh_exchange_identification: <p>Message: Bad request syntax ('SSH-2.0-OpenSSH_6.4').
    
    debug1: ssh_exchange_identification: <p>Error code explanation: 400 = Bad request syntax or unsupported method.
    
    debug1: ssh_exchange_identification: </body>
    
    ssh_exchange_identification: Connection closed by remote host
    

    "Connection established" 表示已经连上端口

    服务器输出

    [wyq@localhost ~]$ python -m SimpleHTTPServer 8080
    Serving HTTP on 0.0.0.0 port 8080 ...
    127.0.0.1 - - [22/Aug/2014 13:32:55] code 400, message Bad request syntax ('SSH-2.0-OpenSSH_6.4')
    127.0.0.1 - - [22/Aug/2014 13:32:55] "SSH-2.0-OpenSSH_6.4" 400 -
    

    用wget判断

    wget是linux下的下载工具,需要先安装.

    用法: wget ip:port

    连接不存在的端口

    [wyq@localhost ~]$ wget localhost:9000
    --2014-08-22 13:36:42--  http://localhost:9000/
    正在解析主机 localhost (localhost)... 127.0.0.1
    正在连接 localhost (localhost)|127.0.0.1|:9000... 失败:拒绝连接。
    

    连接存在的端口

    [wyq@localhost ~]$ wget localhost:8080
    --2014-08-22 13:37:22--  http://localhost:8080/
    正在解析主机 localhost (localhost)... 127.0.0.1
    正在连接 localhost (localhost)|127.0.0.1|:8080... 已连接。
    已发出 HTTP 请求,正在等待回应... 200 OK
    长度:2770 (2.7K) [text/html]
    正在保存至: “index.html”
    
    100%[======================================>] 2,770       --.-K/s 用时 0s      
    
    2014-08-22 13:37:22 (105 MB/s) - 已保存 “index.html” [2770/2770])
    

    总结

    提供端口服务,则使用了tcp协议,上面是以web服务器为例。如果服务器是更简单的tcp服务器,三个工具同样适用.
    三个工具的共同点是:1.以tcp协议为基础;2.能访问指定端口. 遵循这两点可以找到很多工具.
    在windows下使用telnet比较方便,linux下个人就比较喜欢用wget.

  • angularjs指令名是怎么回事?

    疑惑

    查了很多资料,对指令名的介绍都是一笔带过,只说是驼峰形式. 但是在实际使用时,经常遇到定义的指令名与指令标签对应不上的情况. 对指令名就感到非常疑惑. 定义时指令名是一种形式,使用时又是一种形式,两者怎么关联对应的?

    找不到资料,自己查查angular源码,一探究竟.

    分析源码

    首先在angular.js文件,找到解析指令名的代码

    switch(nodeType) {
      case 1: /* Element */
        // use the node name: <directive>
        //此处是解析标签形式的指令
        addDirective(directives,
            directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority, ignoreDirective);
    
        // iterate over the attributes
        for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
                 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
          var attrStartName = false;
          var attrEndName = false;
    
          attr = nAttrs[j];
          if (!msie || msie >= 8 || attr.specified) {
            name = attr.name;
            value = trim(attr.value);
    
            // support ngAttr attribute binding
            ngAttrName = directiveNormalize(name);
            if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
              name = snake_case(ngAttrName.substr(6), '-');
            }
    
            var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
            if (ngAttrName === directiveNName + 'Start') {
              attrStartName = name;
              attrEndName = name.substr(0, name.length - 5) + 'end';
              name = name.substr(0, name.length - 6);
            }
    
            //此处是解析属性形式的指令名
            nName = directiveNormalize(name.toLowerCase());
            attrsMap[nName] = name;
            if (isNgAttr || !attrs.hasOwnProperty(nName)) {
                attrs[nName] = value;
                if (getBooleanAttrName(node, nName)) {
                  attrs[nName] = true; // presence means true
                }
            }
            addAttrInterpolateDirective(node, directives, value, nName);
            addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
                          attrEndName);
          }
        }
    

    上面一处是解析标签形式的指令名,调用了directiveNormalize(nodeName_(node).toLowerCase())
    另一处是解析属性形式的指令名,调用了directiveNormalize(name.toLowerCase())
    两处都使用了toLowerCase()
    所以解析指令的第一步:将指令名转为小写.

    继续看directiveNormalize()函数

    var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i;
    /**
     * Converts all accepted directives format into proper directive name.
     * All of these will become 'myDirective':
     *   my:Directive
     *   my-directive
     *   x-my-directive
     *   data-my:directive
     *
     * Also there is special case for Moz prefix starting with upper case letter.
     * @param name Name to normalize
     */
    function directiveNormalize(name) {
      return camelCase(name.replace(PREFIX_REGEXP, ''));
    }
    
    

    它使用用了name.replace(PREFIX_REGEXP, ''),它的作用是去掉x和data开始的前缀 所以解析指令的 第二步:去掉以x-和data-开头的前缀(例如 x- x: x_ data- data: data_ 忽略大小写)

    再继续看camelCase()函数

    var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
    var MOZ_HACK_REGEXP = /^moz([A-Z])/;
    var jqLiteMinErr = minErr('jqLite');
    
    /**
     * Converts snake_case to camelCase.
     * Also there is special case for Moz prefix starting with upper case letter.
     * @param name Name to normalize
     */
    function camelCase(name) {
      return name.
        replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
          return offset ? letter.toUpperCase() : letter;
        }).
        replace(MOZ_HACK_REGEXP, 'Moz$1');
    }
    

    使用了两个replace替换字符,第二个replace不用管,重点看第一个replace,它是什么作用?

    把它拿出来看看

    var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
    function camelCase(name) {
      return name.
        replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
          console.log("_:" + _)
          console.log("separator:" + separator)
          console.log("letter:" + letter)
          return offset ? letter.toUpperCase() : letter;
        }).
    }
    console.log("----my-directive")
    console.log(camelCase(my-directive));
    
    console.log("----mydirective")
    console.log(camelCase(my-directive));
    
    console.log("----mydirectiveworld")
    console.log(camelCase(my-directive));
    /*
    输出内容
    ----my-directive
    _:-d
    separator:-d
    letter:d
    myDirective
    
    ----mydirective
    mydirective
    
    ----mydirectiveworld
    mydirectiveworld
    */
    

    可以看到第一个replace作用是生成驼峰指令名 所以解析指令的第三步:根据分割符(: - _)标记,将字符转换为驼峰形式

    关于replace函数的说明,可以看看这篇文章 http://yongqing.is-programmer.com/posts/56305.html

    更多指令名与指令对应的示例

    以分割符"-"为例
    mymenu --> mymenu   正确
    mymenu --> myMenu   错误
    mymenu --> my-Menu  错误
    
    myMenu --> my-Menu  正确
    myMenu --> myMenu   错误
    myMenu --> mymenu   错误
    
    MyMenu --> x-MyMenu 正确
    MyMenu --> MyMenu   错误
    MyMenu --> mymenu   错误
    
    myProductsMenu --> my-Products-Menu   正确
    myProductsMenu --> myProductsMenu     错误
    myProductsMenu --> my-ProductsMenu    错误
    

    总结

    指令的匹配过程
    1. 将指令名转换为小写.
    2. 去掉以x-和data-开头的前缀(例如 x- x: x_ data- data: data_ 忽略大小写) 3. 根据分割符(: - _),转换为驼峰形式
    其实只要注意一点: 匹配过程以定义时的指令名为准,分割符是用来标识驼峰的(angular没有能力识别单词,分割符的一个作用是标识驼峰).

  • javascript replace函数

    定义

    replace()函数用于替换字符串.

    语法

    stringObject.replace(regexp/substr, newSubStr/function)
    

    参数

    regexp/substr       正则表达式/字符串  
    newSubStr/function  替换文本/生成替换文本的函数
    

    示例

    var str = "Visit Microsoft!";
    var res = str.replace("Microsoft", "W3Schools");
    

    替换字符

    假如替换字符newSubStr中包含$符号,它表示什么?
    $1、 $2、 ...、$99 与 regexp 中的第1到第99个子表达式相匹配的文本
    $& 与 regexp 相匹配的子串
    $` 位于匹配子串左侧的文本
    $' 位于匹配子串右侧的文本
    $$ 直接量符号。

    上面$1、$2...中提到的子表达式是什么意思?正则表达式中用小括号包裹起来的表达式.
    例如:
    /a/ 无子表达式
    /[a]/ 无子表达式
    /(a)/ 有子表达式(a)
    /(a), (b)/ 有子表达式(a)、(b)

    示例

    name = "Doe, John";
    name.replace(/(\w+)\s*, \s*(\w+)/, "$2 $1");
    //输出"John Doe"
    

    替换函数

    替换函数说明

    function(match, p1, p2,..., offset, string)
    

    match 匹配到的字符串 p1, p2... 匹配到的子表达式 offset 匹配到字符串的位置 string 整个字符串

    示例

    var name = "ace:mm-Abc".replace(/([\:\-\_]+(.))/, function(match, p1, p2, offset, total){
            console.log("match:" + _);
            console.log("p1:" + separator);
            console.log("p2:" + letter);
            console.log("offset:" + offset);
            console.log("total:"+ total)
            return offset ? p2.toUpperCase() : p2;
    })
    console.log(name)
    /*输出
    substr::m test.html:43
    p1::m
    p2:m
    offset:3
    total:ace:mm-Abc
    aceMm-Abc
    */
    

    上面正则表达式中有两个子表达式
    p1是子表达式([:-_]+(.))匹配到的内容":m"
    p2是子表单是(.)匹配到的内容"m"

  • 你以为你在合群,其实你在浪费青春[转]

    曾经有一个宿舍,宿舍里面八个人。每当宿舍八个人都凑齐的时候,寝室长总会组织一个游戏,就是把八个人分成两组,每组三个人,组织大家打牌,剩下两个人就打开电脑,打起了dota,或者拿出手机不停地刷着网页,或者躺在床上拿着psp等待着他们的轮换。 然后,一晚上就这样过去了。 然后,一年就这样过去了。 然后,四年就这样过去了。

    八个人里面,一定会有一两个人混的还可以,但是也一定会有人混的差。混的可以的,在大学四年,活的多么假:因为他组织别人堕落,自己却坚定的向前,表里不一,活的多么难受。而混的差的,永远不知道问题出在哪里。他根本不知道,他就是跟风了,可是到底哪里出了错误了,他根本不知道。

    最近的课堂上,我不停地强调一点给我的学生,大学期间,你无法选择自己的室友,但是你可以选择自己的朋友。 因为,最近我开始发现,寝室,是堕落的开始;合群,是淘汰的起点。在好多人的字典里面: 四个人,三个人不停地下载着苍井空,第四个人不看,就是不合群。 四个人,三个人打着游戏,第四个人不玩儿,就是不合群。 四个人,三个人搞着gay,第四个人不搞,就是不合群。 人是怕寂寞的,于是,大多数人都选择合群。 . 可是,你以为你在合群,其实你是在浪费自己的青春; 你以为你交了朋友,当你毕业一无是处时,谁还会把你当朋友; 你以为你大学四年不孤单,当你毕业没有工作时,没有老婆的日子你会更孤单。 有人说孤单痛苦,那谁又说过,实现自己的目标,不会痛苦?

    我短暂的大学期间,目睹了太多为了合群和合污的惨剧,记得大一,总有人叫我打游戏,我也打,可是留下的,是和他们一样的空虚。 记得大二,当他们拿着手机不停下载新的游戏,我在角落却是拿着单词书背单词。 记得大三,寝室七个人对我集体发起攻击,说我不合群。更有人到处说我傲气逼人,到处说我坏话,但是我明白,与众不同,不是我错了,最后我只有申请换寝室。但是现在我明白了,几年后的今天,当一些人在烈日下暴晒时,我却在空调房写文章。

    最重要的是,我已经忘记了当时说我不合群那些人的名字是什么。我知道,他们中有可能还有人惦记着我,盼望我早点死掉,但是我只想说,他们惦记着我,说明他们生活里面不能没有我;而我忘记了他们的名字,说明我的生活里面可以没有他们。 直到今天,我认识了许多人,有些是有名的大导演,有些是知名的演员,有些是牛掰的创业家,有些是银行、政界的大亨,有些是当初都不会正眼看我一眼的美女,最重要的是,我交了一帮好朋友。此时此刻我才会感激,当初我没有合群,现在,我才到了属于我自己的群体,去做我应该做的事情。 如果当初我合群,现在身边,又会是谁,又会是什么景象。

    我一直坚信,英雄,永远是孤独的,只有小喽喽才扎堆。“二八定律”永远适合在地球的每一个角落:百分之二十的人,占有百分之八十的资产;百分之八十的人,为百分之二十的服务。 尤其是男孩子,大学四年,一直合群,一直在寝室,一直不打开视野,固步自封,井底之蛙,这一切,总会在今后走进社会的某一时刻一次性还给自己。 而女孩子,更是需要在大学中培养出独立的人格,依靠一个男人,永远比不上依靠一个自己双手创造的未来踏实。 但是,我想说,我这里说的不合群,不是结仇,不是桀骜不驯。这里,我在大学做的不够,我检讨。至少,千万不要得罪人,因为道不同,不为谋。但是不代表连话都不讲,或者恶语相向。你支持他的生活模式,只是你需要拥有自己的思想。

    这个世界很邪门,你永远不会相信,当年最混蛋的那个人,十年后会是政治界最有潜力的谁;你也不会相信,当年最不合群的人,成为了百万富翁。 无论如何,那些有点成就的人,都不合群;就算表面合群,他们内心,也总有着自己的一片世界,他们喜欢静静的思考,并且一直向它迈进。

  • mkstemp、mktemp、TemporaryFile区别

    MKSTEMP

    fd, name = tempfile.mkstemp 创建临时文件,并且将文件打开

    >>> import tempfile
    >>> tempfile.mkstemp()
    (3, '/tmp/tmpkgWSR1')
    

    查看/tmp目录,看到已经生成真实的文件

    lsof查询打开的临时文件

    [wyq@localhost tmp]$ lsof|grep tmp|grep python
    python    8095           wyq    3u      REG               0,33         0     254593 /tmp/tmpkgWSR1
    

    发现mkstemp不仅创建文件,而且将文件打开. 使用mkstemp很容易忘了这点,最终造成OSError: [Errno 24] Too many open files错误.
    mkstemp返回的是文件描述和文件路径,并不常用,常用的是下面两个.

    MKTEMP

    name = tempfile.mktemp 返回一个临时文件的路径,但不创建该临时文件

    >>> import tempfile
    >>> tempfile.mktemp()
    '/tmp/tmpPVidBM'
    

    仅仅生成临时文件名

    TEMPORARYFILE

    tempfile.TemporaryFile 返回文件对象(file-like)用于临时数据保存。当文件对象被close或者被del的时候,临时文件将从磁盘上删除

    import time
    with tempfile.TemporaryFile(mode='w+r') as f:
        f.write("=============")
        f.seek(0)
        print f.read()
        time.sleep(10)
    

    TemporaryFile并未在/tmp目录中创建临时文件,应该只存在与内存中.

  • 理解指令的restrict属性

    restrcit属性说明

    restrict: EACM中的任意一个之母。它是用来限制指令的声明格式的。

    E - 元素名称:<my-directive></my-directive>
    A - 属性: <div my-directive="exp"> </div>
    C - 类名:<div class="my-directive: exp;"></div>
    M - 注释: <!-- directive: my-directive exp -->
    

    它做了什么

    • 示例
    <html ng-app='app'>
    <body>
        <hello> </hello>
        <div hello> </div>
        <div class="hello"> </div>
        <!-- directive: hello -->
    </body>
    
    <script src="bower_components/angular/angular.js"></script>
    <script>
    var appModule = angular.module('app', []);
    appModule.directive('hello', function() {
        return {
            restrict: 'AEC',
            template: '<h3>Hi there</h3>',
            replace: true
        };
    });
    </script>
    </html>
    
    • 运行结果
    <h3>Hi there</h3>
    <h3 hello>Hi there</h3>
    <h3 class="hello">Hi there</h3>
    <h3>Hi there</h3>
    

    可以看到几种方式,做的事情一样,只有部分区别. 这些区别有什么作用,用在什么场合?

    使用场合

    • restrict=E时,浏览器无法识别指令的声明元素,那么可以知道这个指令一定是起 替换作用 ,也就是说template一定有值.
    • restrict=A时,指令是以元素属性形式存在的,这个指令的作用则可以不是替换作用. 那么它可以做什么? 以link方式操作dom
      比如为元素聚焦
    <input type="input" focus/>
    
    appModule.directive('focus', function() {
        return {
            restrict: 'A',
            link:function(scope, elem, attrs){
                $(elem).focus();
            }
        };
    });
    
    • restrict=C,则是在绑定指令的同时,指定它的css样式,让指令与样式同步.
    • restrict=M,则在一些场合非常有用,方便在注释与代码之间切换.
  • webpy在insert时psycopg2.OperationalError: ...currval 仍没被定义

    在用webpy的db.insert()向postgres插入数据时出现

    Traceback (most recent call last):
      File "upgrade2.0.py", line 170, in <module>
        copy_webservice()
      File "upgrade2.0.py", line 165, in copy_webservice
        db36.insert('top_node_location', **r)
      File "/usr/lib/python2.7/site-packages/web/db.py", line 777, in insert
        self._db_execute(db_cursor, sql_query)
      File "/usr/lib/python2.7/site-packages/web/db.py", line 587, in _db_execute
        out = cur.execute(query, params)
      File "/usr/lib/python2.7/site-packages/DBUtils/SteadyDB.py", line 631, in tough_method
        raise error # reraise the original error again
    psycopg2.OperationalError: 错误:  在此会话中序列 "top_node_location_id_seq" 的 currval 仍没被定义
    

    找到/lib/python2.7/site-packages/web/db.py

    def _process_insert_query(self, query, tablename, seqname):
        if seqname is None:
            # when seqname is not provided guess the seqname and make sure it exists
            seqname = tablename + "_id_seq"
            if seqname not in self._get_all_sequences():
                seqname = None
    
        if seqname:
            # 注释掉此句
            #query += "; SELECT currval('%s')" % seqname
            pass
    
        return query
    

    原因是webpy在insert时,有一个额外的select使用currval获取当前序列值,但是在postgres9.3中已经不支持currval()了.
    看来webpy作者去世,后面没有更新维护了

  • python终端模拟工具pexpect

    Pexpect是一个纯Python模块。 可以用来和ssh、ftp、passwd、telnet 等命令行程序进行自动交互。实现与命令行交互的自动化.

    它提供了两个主要接口 run()函数和spawn类.

    run()函数

    run功能相对简单,只能实现简单交互

    run(command,timeout=-1,withexitstatus=False,events=None,extra_args=None, logfile=None, cwd=None, env=None)
    

    run运行命令,然后返回结果,与os.system类似.

    示例

    pexpect.run('ls -la')
    
    # 返回值(输出,退出状态)
    (command_output, exitstatus) = pexpect.run('ls -l /bin', withexitstatus=1)
    

    spawn类

    spawn功能比run强大,可以实现更复杂交互

    class spawn
        __init__(self, command, args=[], timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None)
    
    • timeout指定交互是等待的超时值
    • maxread设置read buffer大小. 每次pexpect尝试从TTY(Teletype终端)从读取的最大字节数.
    • searchwindowsize 指定了从输入缓冲区中进行模式匹配的位置,默认从开始匹配.
    • logfile 参数指定日志的记录位置

    工作过程

    # 第一步与终端建立连接
    child = pexpect.spawn('scp foo user@example.com:.')
    # 第二步等待终端返回匹配内容
    child.expect('Password:')
    # 第三步根据返回内容发送命令进行交互
    child.sendline(mypassword)
    

    pxssh类

    pxssh是pexpect的派生类,用于建立ssh连接,比pexpect好用.
    * login() 建立到目标机器的ssh连接
    * logout() 释放该连接
    * prompt() 等待提示符,通常用于等待命令执行结束

    工作过程与pexpect相同

    # 初始化pxssh示例
    s = pxssh.pxssh()
    
    hostname = raw_input('hostname: ')
    username = raw_input('username: ')
    password = getpass.getpass('password: ')
    # 建立ssh连接
    s.login (hostname, username, password)
    # 发送命令到远程终端
    s.sendline ('uptime') # run a command
    # 等待远程终端返回
    s.prompt() # match the prompt
    # 打印匹配到的内容
    print s.before  # print everything before the propt.
    
    # 发送命令到远程终端
    s.sendline ('ls -l')
    # 等待远程终端返回
    s.prompt()
    # 打印匹配到的内容
    print s.before
    
    s.sendline ('df')
    s.prompt()
    print s.before
    
    # 释放ssh连接
    s.logout()