Flask路由使用装饰器的问题

需要实现验证用户登录的功能,考虑用装饰器实现:

def check_permission(func):
    def _check_permission(**args):
        #do check...
        return func(**args)
    return _check_permission

@app.route('/routea')
@check_permission
def a():
    pass

@app.route('/routeb')
@check_permission
def b():
    pass

当@check_permission只有一个时可以正常使用,但是超过一个就会报错:

AssertionError: View function mapping is overwriting an existing endpoint function: _check_permission

这样的错误是因为Flask在注册路由的时候使用了函数的名字,这个例子中是a和b,使用装饰器后a、b两个函数的名字都变成了_check_permission,造成冲突,提示overwriting。解决方法是加上@wraps():

from functools import wraps
def check_permission(func):
    @wraps(func)
    def _check_permission(**args):
        #do check...
        return func(**args)
    return _check_permission

@app.route('/routea')
@check_permission
def a():
    pass

@app.route('/routeb')
@check_permission
def b():
    pass

Python自带辅助库functools中的@wraps装饰器能够改变函数的__name__等,解决问题。具体原理参考下面:

http://coolshell.cn/articles/11265.html
http://stackoverflow.com/questions/19261833/what-is-an-endpoint-in-flask

补充:
完事之后才发现官方文档已经介绍过这个方法了,事实证明我是对的……

http://docs.jinkan.org/docs/flask/patterns/viewdecorators.html

没钱就用Arch!

在办公室找到一个2007年的完好的古董PC,然后就生命在于折腾啦。

系统安装

机器只有一个CD光驱,DVD碟读不出来,改用U盘但是引导不进去。拔掉硬盘线通过SATA to USB转接器接在笔记本电脑上,打开主机供电,成功读出硬盘。笔记本插上安装碟进了ArchLinux,/dev下果然有两个硬盘sda、sdb,转接器支持得还不错。格式化之后分好区,跟着ArchWiki走顺利装好系统。不料重启之后进了Grub,手动引导成功进入系统,Grub就懒得修复了反正又不重启。

Awesome

为了分屏+纯键盘操作+低性能支持用了Awesome,原来在笔记本上没能体会到它的优点,现在装在低性能无鼠标的环境上十分带感。作死忘加交换区,装了FireFox打开就死机,添加4G交换区之后吃上100MB的还能正常运行,趁热把密钥生成好导入到Github上。
Awesome的rc.lua配置很强大,简单配置一下就能发挥大作用。为了完全的自定义用lua脚本来做配置文件也是醉了。
装Awesome花了大量时间,主要是忘了装X嗯,下次记住了,xorg-apps也要装上。

终端

试过好多终端软件,回头发现Awesome的自带终端完全不需要换,改改颜色改改字体还能再战。
不仅主机是老古董,显示器也是旧的,KTC 7009S,1280×1024,作死的5:4。分辨率太低没办法,把终端字号改成8px,勉勉强强Vim可以开三个竖排。
中文支持很麻烦,改了LANG装了WenQuanYi,少数情况下依然有方块。

Vim

刚说了显示器很烂,Vim下的配色试了许多,花花绿绿的很刺眼。最后选了molokai,普通语句和字符串都是不刺眼的颜色,Python下def、class、return、if这些出现频率不高的关键字是红色和黄色。
考虑过用Vim来分屏还是Awesome。Vim开:vs分屏之后,中间的分界线是很粗的一条白色线条,在整个暗色的编辑环境里十分碍眼。Awesome本身对分屏支持也更好,怒选Awesome。不知道白条可不可以自定义。

目前的环境

工作台
左边是刚装好的Arch,右边是笔记本Windows。Arch写代码Win搜资料,大家都是程序员为什么要相互伤害,Linux和Win不是配合得好好的吗233333
不管怎么说环境都是自己营造的啦,虽然再恶劣也能靠自己的一点一滴来改变。没钱就用Arch!

Node_redis对数组支持的BUG

做微信公众平台的时候发现Node_redis(https://github.com/mranney/node_redis)一个严重BUG
执行redis.lpush(‘mylist’,[‘a’,’b’,’c’],callback)的时候,预计返回

mylist
a
b
c

实际返回

mylist
a,b,c

Github已经有人提出过这个问题

https://github.com/mranney/node_redis/issues/369#issuecomment-13050944

产生原因

所有的client.COMMAND会调用client.send_command()

RedisClient.prototype[command] = function (args, callback) {
    if (Array.isArray(args) && typeof callback === "function") {
        return this.send_command(command, args, callback);
    } else {
        return this.send_command(command, to_array(arguments));
    }
};

调用client.COMMAND的时候

client.COMMAND(key, ["arg1", "arg2", "arg3"], callback);

会被解析成

client.send_command(COMMAND, [key, ["arg1", "arg2", "arg3"]], callback);

send_command()有如下段落

    for (i = 0, il = args.length, arg; i < il; i += 1) {
        arg = args[i];
        if (typeof arg !== "string") {
            arg = String(arg);
        }
        command_str += "$" + Buffer.byteLength(arg) + "\r\n" + arg + "\r\n";
    }

示例中循环到第二次 arg = ["arg1", "arg2", "arg3"] ,会被String()强制转换为"arg, arg2, arg3",产生BUG。

解决方法

直接调用

client.send_command(COMMAND, [key, "arg1", "arg2", "arg3"], callback);

或者将key包含到数组里面

client.COMMAND([key, "arg1", "arg2", "arg3"], callback);

吐个槽

既然都知道原因了,2013年2月的issue为什么到现在都还没解决!!

系统引导流程、重建和Grub

手抖格式化了硬盘,好在及时停止,只是引导被破坏。原系统中有Win7+Ubuntu双系统,Win7在/dev/sda1,Linux在/dev/sda7。重装引导的时候出了不少问题,也得到了很多新的认识。

Grub和Grub2

Grub有Grub和Grub2。Grub2在10年前已经发布,目前预装在了大多数发行版上。网上各种教程多为Grub的而不是Grub2。
Grub和Grub2有很大区别,经常碰到的有:

  • /boot/grub/menu.lst被/boot/grub/grub.cfg所取代
  • update-grub被grub-mkconfig -o /boot/grub/grub.cfg取代
  • Grub中加载内核命令kernel在Grub2中有相似的命令linux

已经入过一次坑,请大家明鉴。

乱糟糟的中文wiki:http://linux-wiki.cn/wiki/zh-hans/Grub2%E9%85%8D%E7%BD%AE
grub和grub2的区别:http://lesca.me/archives/differences-between-grub-and-grub2.html

MBR和各种启动器的关系

each disk has an mbr (master boot record) or vbr (volume boot record)
– a partition does not have an mbr or vbr

the mbr is in the first 512 bytes of a disk and is not normally
accessible

the mbr (basically) holds the information for your disk and partitions
and where to find the boot files

bcdedit in win7 and vista is the boot configuration data editor, this
has replaced ntldr (new technology loader) that is in xp and w2k
linux uses grub (grand unified bootloader) or lilo (linux loader)

all 4 of these write to your mbr so your mbr can access the boot
configuration file (boot.ini in xp/w2k, menu.lst for grub legacy or
grub.conf for grub2 in linux, etc) and allow the system to boot,

windows bootloaders do not recognise other systems so cannot boot
non-windows systems, grub can be installed on its own and can boot
any operating system,
原文:http://answers.yahoo.com/question/index?qid=20100902034736AAUYSXC

大致翻译

每块硬盘都有一个MBR(Master Boot Record,主引导记录)或者VBR(Volume Boot Record,卷引导记录)。
MBR在磁盘的头512字节(这就是为什么低级格式化磁盘造成MBR损坏的原因),保存了磁盘和分区的启动信息。
winxp、win2k使用ntldr(New Technology Loader),Win7、Vista使用bcdedit来作为引导编辑器,Linux一般使用grub、lilo等。
以上这四个工具会写MBR来引导启动相应的操作系统。但是Win的引导器只能引导Win的操作系统,Linux的例如grub2可以引导Win、Linux甚至Mac。

MBR由三个部分组成:

  1. 第1-446字节:调用操作系统的机器码
  2. 第447-510字节:分区表(Partition table)
  3. 第511-512字节:主引导记录签名(0x55和0xAA)

http://zh.wikipedia.org/wiki/MBR

启动流程

fig1.gif

  1. BIOS
  2. MBR中的第一部分机器码执行
  3. 各种启动器(grub、lilo……)
  4. 操作系统等

这样看来系统分区设为活动分区并不是必须的,这主要视引导程序而定,有些引导程序例如Grub4Dos仅仅遍历所有分区并运行第一个找到的引导器grldr。同样,grub写入MBR中的程序会搜寻逻辑分区,所以将grub主程序安装在逻辑分区(/dev/sda7)里面依然能够正常启动。

Grub2重建引导

Grub2中重建引导命令

grub-install --root-directory=/ /dev/sda
--root-directory是指安装的位置,一般安装在分区根目录下
/dev/sda是MBR的写入磁盘,是整个磁盘而不是分区

例如单硬盘机器Linux安装在/dev/sda7:

mount /dev/sda7 /mnt
grub-install --root-directory=/mnt/ /dev/sda

这样可以将Grub2安装在Linux分区里面,当然也可以安装在其他分区。Grub2会自动搜索NTFS分区, 理论上安装好后不用配置就可以引导Windows。(有些地方似乎是grub2-install)
很多教程将命令写作

grub-install --boot-directory=/boot

不知道是否有这个用法,但是我没成功。
另外

grub-update 自动更新启动项目列表,添加有效操作系统(有的地方写作update-grub)

重建引导都干了什么

执行grub-install之后会重写MBR的第一部分,这样计算机启动的时候就会执行重写过后的机器码,这段机器码会将控制权交给grub主程序,这里是/dev/sda7里的/boot/grub(当然/boot/grub也会在install的时候重新安装)。开始第二阶段引导加载。

Linux 引导过程内幕https://www.ibm.com/developerworks/cn/linux/l-linuxboot/

让织梦缩略图自适应裁剪

Ecjtu.net有三个页面需要调用同一个文章的缩略图,但大小比例都不一样。用如下方法实现缩略图自动裁切。

需要PHP安装GD库,http://www.php.net/manual/zh/intro.image.php

代码

/include/extend.func.php里添加thumb()函数:

function thumb($imgurl, $width, $height)
{
    global $cfg_mainsite,$cfg_multi_site;
    $thumb = eregi("http://",$imgurl)?str_replace($cfg_mainsite,'',$imgurl):$imgurl; 
    list($thumbname,$extname) = explode('.',$thumb);
    $newthumb = $thumbname.'_'.$width.'_'.$height.'.'.$extname;
    if(!$thumbname || !$extname || !file_exists(DEDEROOT.$thumb)) return $imgurl;
    if(!file_exists(DEDEROOT.$newthumb))
    {
        include_once DEDEINC.'/image.func.php';
        $src_im = imagecreatefromjpeg(DEDEROOT.$thumb);
        $dst_im = imagecreatetruecolor($width, $height);
        $width_im = imagesx($src_im);
        $height_im = imagesy($src_im);
        if($width_im/$height_im < $width/$height)
        {
            $tmp = ($height_im-$width_im*($height/$width))/2;
            imagecopyresampled($dst_im, $src_im, 0, 0, 0, $tmp, $width, $height, $width_im, $height_im - $tmp*2);
        }
        else
        {
            $tmp = ($width_im-$height_im*($width/$height))/2;
            imagecopyresampled($dst_im, $src_im, 0, 0, $tmp, 0, $width, $height, $width_im - $tmp*2, $height_im);
        }
        imagejpeg($dst_im, DEDEROOT.$newthumb, 100);
        imagedestroy($dst_im);
        imagedestroy($src_im);
    }
    return $cfg_multi_site=='Y'?$cfg_mainsite.$newthumb:$newthumb;
}

调用

在任何调用缩略图的时候:

[field:picname function='thumb(@me,590,310)'/]
第二个第三个参数分别为宽高

规则

调用缩略图的时候首先查看缩略图目录是否有相应大小裁切过的图片,有就直接返回,没有就在同目录下生成并返回。
生成的缩略图命名规则:

原缩略图名_宽_高.xxx
例:142_1321254421_590_310.jpg

裁切规则:

  • 保持需要的比例
  • 尽可能大的保留图片
  • 超出尺寸部分裁剪两边保留中间

注意

相应尺寸的缩略图生成之后就永远不会被改变,因此某些特殊情况下可能需要你手动删除。