多国语言环境下 Jinja2 与 strftime 互动异常退治纪要
不少语言或者库或多或少都遭遇过乱码问题. 然而这一点在 Python 里面表现得比较另类, 一般来说只要贯彻 UTF-8 到底的思路, 不怎么可能遇到一大波乱码袭来的情况. 只是, 偶尔会直接因为 字符串类型设置不对而直接抛运行期异常. 常在河边走, 湿鞋是顺理成章的事情. 这次遇到的情况简报如下 # encoding=utf-8
import jinja2 import datetime
now = datetime.datetime.utcnow()
print jinja2.Template(u'''当前月份 {{ date.strftime('%Y 年 %m 月') }}''').render(date=now)
运行这一段代码, Python 直接扔出来一坨 Traceback (most recent call last): File "test.py", line 8, in <module> print jinja2.Template(u'''当前月份 {{ date.strftime('%Y 年 %m 月') }}''').render(date=now) File "/usr/local/lib/python2.7/dist-packages/Jinja2-2.6-py2.7.egg/jinja2/environment.py", line 894, in render return self.environment.handle_exception(exc_info, True) File "<template>", line 1, in top-level template code UnicodeEncodeError: 'ascii' codec can't encode character u'\u5e74' in position 3: ordinal not in range(128)
简单地说, 就是 Jinja2 在刷什么东西的过程中挂了, 因为字符串里面混入了什么奇怪的东西. 一番尝试并寻找之后, 首先发现第一个问题, datetime.datetime.strftime 函数接受奇怪的字符串会出问题, 比如先来试试看这个 now = datetime.datetime.utcnow() print now.strftime('%Y 年 %m 月')
结果什么问题都没有, 突然有人品爆了的感觉, 这个好像很不科学的样子啊. 再仔细对比代码找找茬才发现要这样才能挂掉程序 now = datetime.datetime.utcnow() print now.strftime(u'%Y 年 %m 月')
这前缀 u 的 unicode 类型怎么说也坑太深了的感觉吧. (别扯什么 Python str 与 unicode 类型异与同 21 天从入门到精通什么的, 别的语言都没这问题 Python 确有, 这应该就是 Python 的问题了吧.) 在这种情况下如果要用 unicode 还得转一道 now = datetime.datetime.utcnow() print now.strftime(u'%Y 年 %m 月'.encode('utf-8'))
Posted at Apr 13 2013 - 09:21:26
Permanent Link:
/p/510
Load full text
|
Post tags:
Python
Jinja2
strftime
Unicode
|
Flask 出坑记
Flask 是个 Python Web 框架. 网站上文档例子都很详尽, 这里就不废话了, 只是来扯两个使用中需要注意的地方. 获取 POST 请求体 21 世纪的 Web 交互中服务器跟浏览器互相丢 JSON 已经成了司空见惯的事情. 在 Flask 框架作成的服务器上要搞到 JSON 数据当然是直接访问 POST 请求体了, 如 import flask import functools
app = flask.Flask(__name__)
@app.route('/wtf', methods=['POST']) def wtf(): return 'Received: ' + flask.request.data
def main(): app.run(port=7777)
if __name__ == '__main__': main()
按 文档的说法, flask.request.data 包含请求数据字符串. 但其实这也是个坑, 默认情况下根本 取不到请求数据$ curl -d "[1,1,2,3,5,8]" http://localhost:7777/wtf Received:
熊孩子你把拿到的字符串给吃了吧? 实际上如果去看看那文档会看到并不如上面所说的那样, 而是 - Contains the incoming request data as string in case it came with a mimetype Flask does not handle.
后面这个状语从句真是着急, 那到底什么 mimetype 会使得 Flask does not handle 呢? 根本没说清楚啊. 扫一眼文档后面, 还有个东西可以用: flask.request.json , 但这货一般是 None , 只有当请求 mimetype 被设置为 application/json 的时候才有用, Flask 你真心是跟 mimetype 过不去啊. 也就是说得这样发请求 $ curl -d "[1,1,2,3,5,8]" localhost:7777/wtf Received: null $ curl -d "[1,1,2,3,5,8]" -H "Content-Type:application/json" localhost:7777/wtf Received: [1, 1, 2, 3, 5, 8]
# Server codes
import json import flask import functools
app = flask.Flask(__name__)
@app.route('/wtf', methods=['POST']) def wtf(): return 'Received: ' + json.dumps(flask.request.json)
def main(): app.run(port=7777)
if __name__ == '__main__': main()
问题是现在前端攻城狮都被浏览器兼容性折腾得满世界买表, 哪还有心情检查每个请求的 content-type 对不对. 况且这还只对 JSON 有效, 如果是山寨协议又怂了. 好吧, 如果实在不行, 就挖到 WSGI 里面去好了, 比如这样 def request_data(): d = flask.request.data if not d: return ''.join(flask.request.environ['wsgi.input'].readlines()) return d
Posted at Apr 05 2013 - 02:49:05
Permanent Link:
/p/509
Load full text
|
Post tags:
Python
Flask
Web Server
|
移花接木 - 编译同步代码为异步代码 [条件表达式篇]
接 上篇条件表达式 如果设计如下的类生成 Javascript 中条件表达式 predicate ? consequence : alternative # semantic.py
class Conditional(Expression): def __init__(self, predicate, consequence, alternative): self.predicate = predicate self.consequence = consequence self.alternative = alternative
def compile(self, context): return output.Conditional(self.predicate.compile(context), self.consequence.compile(context), self.alternative.compile(context))
# output.py
class Conditional(Expression): def __init__(self, predicate, consequence, alternative): self.predicate = predicate self.consequence = consequence self.alternative = alternative
def str(self): return '({p} ? {c} : {a})'.format(p=self.predicate.str(), c=self.consequence.str(), a=self.alternative.str())
这在条件表达式的三个子表达式均不包含异步调用时, 或仅 predicate 包含正规异步调用没问题, 如 root_context = ContextFlow() root_flow = root_context.block
Block([ Arithmetics( Binary( '=', Reference('m'), Conditional( Reference('a'), Reference('b'), Reference('c')))), Arithmetics( Binary( '=', Reference('n'), Conditional( RegularAsyncCall( Reference('f'), [ NumericLiteral(0) ]), Reference('x'), Reference('y')))), ]).compile(root_context)
print root_flow.str()
这些节点编译后会生成类似如下代码
Posted at Mar 17 2013 - 00:38:47
Permanent Link:
/p/508
Load full text
|
Post tags:
Python
Javascript
Asynchronous Expression
Compiler Construction
|
移花接木 - 编译同步代码为异步代码 [补述篇]
接 上篇编译步骤解释多个正规异步调用出现在同一个语句中的编译顺序 RegularAsyncCall 对象的 compile 代码如下 def compile(self, context): # 0 compl_callee = self.callee.compile(context)
# 1 compl_args = [ arg.compile(context) for arg in self.arguments ]
# 2 callback_body_context = ContextFlow() cb_body_flow = callback_body_context.block compl_args.append(output.Lambda([ 'error', 'result' ], cb_body_flow))
# 3 context.block.add(output.Arithmetics(output.Call(compl_callee, compl_args)))
# 4 context.block = cb_body_flow
return output.Reference('result')
步骤依次是 - 0 编译被调用对象
- 1 依次编译调用实参
- 2 向实参列表中追加一个匿名函数, 函数体语句流是新建的一个
ContextFlow 中的语句流 - 3 向当前上下文语句流中插入一个算术节点, 节点的内容是编译后的异步调用
- 4 修改上下文语句流
它们的顺序不可随意颠倒, 不仅仅因为有些步骤依赖于前面的步骤 (比如步骤 3 构造 Call 对象须 0 与 1 中得到的结果), 更多地是因为在编译过程中上下文语句流随时可能变化. 比如下面这个例子 async_call('xx', %%)('oo', %%)
这应当生成下面这段 Javascript 代码 async_call('xx', function(error, result) { result('oo', function(error, result) { result; }); });
async_call('xx', %%) 编译时第一次修改了上下文语句流, 而之后对前一次异步调用结果的调用再一次修改了上下文语句流. 用语法树来表示即 +-------------+ | origin flow | +-------------+ | +-------------+ +---------| arithmetics | +-------------+ | [A]====================+ | regular async call | +====================+ / \ [B]====================+ +------+ | regular async call | | 'oo' | +====================+ +------+ / \ +------------+ +------+ | async_call | | 'xx' | +------------+ +------+
Posted at Mar 13 2013 - 02:05:29
Permanent Link:
/p/507
Load full text
|
Post tags:
Python
Javascript
Asynchronous Expression
Compiler Construction
|
移花接木 - 编译同步代码为异步代码 [数据结构篇]
目标 我说 JS 代码的表达能力是一流的, 可能一票 Python 党 (作为高级黑, 我不得不说我自己也是个 Python 党) 要笑了. 我是说真的, Python 的 Lambda 基本上就是半残废, 所以, 要用 Python 写异步代码那就像是用挂面上吊一样不来劲了. 然而任何写 JS 代码遇到三层以上的异步代码套在一起的时候手撕键盘的心都有了, 蔽博先前也介绍了一个工具 (确切地说是 玩具) 用来 使用类似同步的代码生成异步代码. 而现在则有更明确的目标, 如把这样一段代码 fs: require('fs') content: fs.readFile('/etc/passwd', %%) console.log(content)
变成这样一段 Javascript var fs, content; fs = require('fs'); fs.readFile('etc/passwd', function(err, result) { content = result; console.log(content); });
为什么是这样? 首先 JS 中很多异步调用的回调函数都遵循 function(error, result) 这个形式, 这俨然成为了 Javascript 中的一种约定, 因此在接下来的分析中把这样的回调函数参数称为 正规异步回调 (regular asynchronous callback). 既然它们都是这么个形式, 那么写代码的时候完全可以不这么罗嗦写个回调声明, 而是用 %% 这个符号来替代它. 而包含正规异步回调的函数调用称之为 正规异步调用 (regular asynchronous call). 更详细地说, fs.readFile('/etc/passwd', %%) 这个表达式的意思是 - 调用
fs.readFile - 第一个参数是字符串常量
'/etc/passwd' - 第二个参数是一个正规异步回调函数
- 从这个表达式之后, 所有的语句归入上述正规异步回调函数的函数体中
那么下面要做的事请是, 如果在一次编译过程中, 语法分析已经完成, 对应的同步代码的语法树已经建起来, 怎么根据上述规则去生成异步代码. 比如上面的例子, 源语法树形式是
Posted at Mar 11 2013 - 12:48:16
Permanent Link:
/p/506
Load full text
|
Post tags:
Compiler Construction
Python
Javascript
Asynchronous Expression
|
C++ Lambda 简易上手
本文所有代码示例通过 g++ 4.6 编译. 之前有吐槽过 C++ STL 算法中的 for_each 函数华而不实, 主要原因是受限于 C++ 的函数编写语法, 即传统上 C++ 要求函数须独立编写, 而不能嵌套在其它函数内. 不过, 欢迎来到二十一世纪的第一个 0x10 年, 现在有了 lambda. 与其它语言一样, C++ 的 lambda 就是个匿名函数, 它的语法结构如 [ 上下文变量绑定方式 ] ( 形式参数列表 ) 可省略的返回值类型声明 { 函数体 }
匿名函数可以定义在任何地方, 作为一个 表达式出现; 如果在匿名函数的函数体中使用了表达式所在上下文中定义的量, 则需要指定绑定方式. 如 // 定义变量 sqr 为一个匿名函数 // 这个匿名函数的上下文变量绑定方式为空, 因为它无须引入任何上下文变量 // 这个匿名函数接受一个 int 作为参数 // 返回值类型声明省略, 由编译器推倒出返回 int auto sqr = [](int x) { return x * x; };
int x = 10; // 这个匿名函数的上下文变量绑定方式为 & 即引用方式, 它引用了上文中定义的 x // 这个匿名函数接受一个 int 作为参数 // 返回值类型声明为 bool auto equalsToX = [&](int y) -> bool { return x == y; };
// 这个匿名函数的上下文变量绑定方式为 = 即复制方式, 它引用了上文中定义的 x // 这个匿名函数接受一个 int 作为参数 // 返回值类型声明省略 auto lessThanX = [=](int y) { return y < x; };
可见多亏了 C++ 的强制类型声明和变量定义检查, 才把一个简单的匿名函数搞得这么复杂, 解决了不少其它语言中不存在的问题. 作为 C++ 程序员的一个特异能力便是能够在写代码时就预见到这段程序运行时内存与对象的流动. 看了上面匿名函数的各种奇葩绑定上下文的方法, 很明显它们不仅是一个函数那么简单, 如果必要匿名函数会占用一些内存空间, 可以做这样一个实验来试试. struct Point { Point() : x(0) , y(0) {}
double x; double y; };
int main() { Point p; auto f = [&]() { std::cout << p.x << "," << p.y << std::endl; }; auto g = [=]() { std::cout << p.x << "," << p.y << std::endl; }; std::cout << sizeof(f) << " - " << sizeof(Point*) << std::endl; std::cout << sizeof(g) << " - " << sizeof(Point) << std::endl;
auto sqr = [](int x) { return x * x; }; std::cout << sizeof(sqr) << std::endl;
return 0; }
Posted at Mar 03 2013 - 04:58:20
Permanent Link:
/p/505
Load full text
|
Post tags:
Labmda
C++
C++11
Nested Function
|
填坑 - Stekinscript 异步表达式
深夜潜入一家竞争对手公司, 可惜只偷到了最后一页源代码的打印稿, 回来一看 }); }); }); })();
这是何等的引人泪下的故事. 这不再是 Javascript 刚刚发明的那个年代, 程序员一边用 Java 一边咒骂着花括号嵌套又嵌套何时是个头; 有经验的程序员告诫那些乳臭未干的学生军函数一定要写短, 逻辑多拆分. 有时会想着, 有什么语言会强制规定单一函数的复杂度, 以让每个函数几乎都能限制在一二十行之内呢? 结果现在这个梦想成真了, 对于 Javascript 程序员而言, 这是不得不做的纯天然方式. 比如 Javascript 中采用异步的方式来解决 IO 效率低的问题, 下面是一段用来读取 "/etc/passwd" 内容的 node 代码 fs: require('fs'); fs.readFile('/etc/passwd', function(err, data) { if (err) { return console.error(err); } console.log(data.toString()); });
看着全局空间里洋洋洒洒一大串内容, 其实只有两句话, 第一句引入库, 第二句读文件; 而对文件的处理则单独放在另一个函数了. 多好的强制函数拆分机制, 现在再也不担心老大说函数写太长了. 不过稍微复杂一些的需求就开始疼了, 比如实现从 "/etc/passwd" 中读取前 32 个字节, node 代码为 fs: require('fs'); fs.open('/etc/passwd', 'r', function(err, fd) { if (err) { return console.error(err); } fs.read(fd, new Buffer(32), 0, 32, 0, function(err, bytes, buffer) { if (err) { fs.close(fd); return console.error(err); } console.log(buffer.toString()); fs.close(fd); }); });
望着这回调套回调向着行中陷落下去的代码, 感觉就是挖一个大坑. 各位 Javascript 程序员肯定不希望自己的宝贝女儿在幼儿园被问到「你父亲是做什么的」的时候回答「我也不太清楚但是看起来像是用在电脑里画楼梯的」吧. 所以我想, 得从坑里爬出来. 考虑到「相当一部分的异步调用貌似都是这个语句块的最后一个语句」继而有了「那为什么不在代码里面用标记让一个语句块里的语句实际上是两部分或更多部分, 第一个部分对应于当前语句块, 而后面的每个部分各是一个异步回调的函数体」这样的想法. 有了这个想法后就打起了草稿, 比如上面的前一个例子, 要是写成这样 fs: require('fs') fs.readFile('/etc/passwd', %(err, data)) if err return console.error(err) console.log(data.toString())
感觉如何? %(err, data) 这就是一个 标记, 称之为 异步占位符, 它可以作为函数调用的实参出现 (不能单独写在语句中或者进行算术运算). 异步占位符表示一个匿名函数 (参数列表同异步占位符中标识符列表), 如果一个语句中的某个函数调用中有实参为异步占位符, 那么同一个语句块内, 这个函数调用之后的部分都会被移进异步占位符所表示的匿名函数的函数体中去. 也就是说, 上述代码中, 该匿名函数中的代码会包括 if err return console.error(err) console.log(data.toString())
Posted at Jan 18 2013 - 09:12:14
Permanent Link:
/p/504
Load full text
|
Post tags:
NodeJS
Asynchronous Expression
Stekin
|
在 C++ 模板类体中将类名仍视为模板
用 Clang++ 编译下面的源代码 (已测试 ArchLinux clang 3.2-3) template <template <class> class T> class A {};
template <typename T> class B { A<B> member; };
int main() { B<int> b; return 0; }
会得到如下错误信息 error: template argument for template template parameter must be a class template A<B> member; ^
也就是说, clang++ 会认为出现在模板类 B 的类体中的符号 B 是一个已经实例化过的类型, 不能够被传递给模板类 A 用于实例化. 而 g++ 4.7 可以正确编译通过, 说明 g++ 肯定内置了某种脑补机制, 能灵活处理这样的情况, 它甚至可以编译如下分裂的代码 template <template <class> class T> class A {};
template <class T> class C {};
template <typename T> class B { A<B> member; C<B> another; };
int main() { B<int> b; return 0; }
当然在 clang++ 下也不是没办法解决了, 方案是, 在类体中如果要将类名当作模板来使用, 则在名字前加上名字空间声明. 如上面 B 定义在全局名字空间中, 所以代码应该这样修改 template <template <class> class T> class A {};
template <typename T> class B { A< ::B> member; };
int main() { B<int> b; return 0; }
话说小于号跟双冒号之间有个空格 < :: , 这绝不是我手残了抖上去的, 而是这地方必须要一个空格. C++ 程序设计领域流行着各种都市传说, 其中有模板套模板结束时的两个大于号之间要加个空格什么的, 类似, 小于号跟冒号之间也要加个空格, 否则 <: 会被认为是 [ . 相应地, 如果 B 定义于某个有名字的名字空间 (这话说得...) 中那么上面代码应作相应修改 template <template <class> class T> class A {};
namespace ns { template <typename T> class B { A<ns::B> member; }; }
int main() { ns::B<int> b; return 0; }
Posted at Jan 14 2013 - 03:26:03
Permanent Link:
/p/503
Load full text
|
Post tags:
C++
Template
|
0
1
Page 2
3
4
5
6
7
8
9
10
11
12
|