在 Python 中统计文本字符个数

字符集向来都是一个大问题,即使是 Python 3.x,也最多只是能说感谢 Unicode 字符集,字符串的存取现在没有问题了。

Unicode 字符集的常见编码UTF-8UTF-16UTF-32 等常见格式,另外,GB18030 也可以算其中一种( GB18030,与 UTF-8 类似,是一种变长编码格式,最大的优势就是兼容 GBK/GB2312

但是 Unicode 就能无痛的解决所有问题吗?答案是否定的。

Read More

使用 FPM 创建 Python 的 RPM 包

生成 RPM 包太麻烦了,最近知道了一个名为 FPM 的神器,在此记录一下。

安装 FPM

NOTE: 测试系统为 RedHat 系的 CentOS 6.3,编译 Python 2.7.6 的 RPM 包。

安装 Ruby

由于 FPM 使用 Ruby 写成,因此系统中需要安装 Ruby 的运行环境(这里 gem 的源改为了 taobao 的镜像):

1
2
3
4
5
6
# Install ruby dependencies
yum -y install ruby rubygems ruby-devel
# Use taobao repo for ruby gems
gem sources -a http://ruby.taobao.org/
# Remove origin repo from ruby gems
gem sources --remove http://rubygems.org/

通过 Gem 安装 FPM

Ruby 安装完成后,就可以使用 gem 安装 FPM 了:

1
2
# Install fpm
gem install fpm

Good.

设置编译环境

在编译 Python 之前,需要安装开发工具和库:

1
2
3
4
5
6
7
8
# Install EPEL repository
wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
wget http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
rpm -Uvh remi-release-6*.rpm epel-release-6*.rpm

# Install build toolchain
yum -y groupinstall "Development tools"
yum -y install openssl-devel readline-devel bzip2-devel sqlite-devel zlib-devel ncurses-devel db4-devel expat-devel

编译并创建 Python 的 RPM 包

FPM 的使用比较简单,可以参考 FPM使用说明

首先,下载 Python-2.7.6 的源码包并解压:

1
2
curl --progress-bar -LO http://mirrors.sohu.com/python/2.7.6/Python-2.7.6.tgz
tar xf Python-2.7.6.tgz

第二步,编译 Python-2.7.6

1
2
3
4
5
6
7
8
9
10
11
12
cd Python-2.7.6.tgz

# Python2.7编译安装后会安装到这个目录,方便打包
export INTERMEDIATE_INSTALL_DIR=/tmp/installdir-Python-2.7.6
# RPM包安装后Python2.7的目录
export INSTALL_DIR=/usr/local

LDFLAGS="-Wl,-rpath=${INSTALL_DIR}/lib ${LDFLAGS}" \
./configure --prefix=${INSTALL_DIR} --enable-unicode=ucs4 \
--enable-shared --enable-ipv6
make
make install DESTDIR=${INTERMEDIATE_INSTALL_DIR}

第三步,使用 FPM 创建 RPM 包:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 注意之前导出 INTERMEDIATE_INSTALL_DIR 和 INSTALL_DIR 这两个环境变量,这里还要使用
fpm -s dir -t -f rpm -n python27 -v '2.7.6' \
-d 'openssl' \
-d 'bzip2' \
-d 'zlib' \
-d 'expat' \
-d 'db4' \
-d 'sqlite' \
-d 'ncurses' \
-d 'readline' \
--directories=${INSTALL_DIR}/lib/python2.7/ \
--directories=${INSTALL_DIR}/include/python2.7/ \
-C ${INTERMEDIATE_INSTALL_DIR} .

Bonus Time

包含以下内容:

  • 自动下载、编译、打包 Python RPM 包的 Makefile;
  • 自动下载、编译、打包 virtualenv、pip、supervisor 等 Python 库和工具的 RPM 包。

GitHub 项目地址:python27-rpm

RabbitMQ笔记(三): Pika客户端(Python)发送大尺寸消息的问题

问题描述

这个问题存在很久了,现象就是使用 Pika 库的客户端在发送大尺寸消息后,RabbitMQ 没有收到,Consumer 那里会认为消息已丢失。

NOTE: 即使在本文写时的最新版(v0.9.13)依然存在这个问题。

分析

因为网络状态很好,所以没有考虑网络的错误,直接考虑Pika库的问题。

那么第一步就是把 Pika 的代码拿来读一遍,主要看它是怎么发送消息的,经过一番探索,找到了 pika/adapters/base_connection.py 这个文件,来看看里面的内容:

pika/adapters/base_connection.pylink
1
2
3
4
5
6
7
8
9
10
11
12
def _handle_write(self):
"""Handle any outbound buffer writes that need to take place."""
total_written = 0
if self.outbound_buffer:
try:
bytes_written = self.socket.send(self.outbound_buffer.popleft())
except socket.timeout:
raise
except socket.error, error:
return self._handle_error(error)
total_written += bytes_written
return total_written

看出问题了吗?

来看看socket.send的文档吧:

Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Returns the number of bytes sent. Applications are responsible for checking that all data has been sent; if only some of the data was transmitted, the application needs to attempt delivery of the remaining data. For further information on this concept, consult the Socket Programming HOWTO.

问题就是,没有检查 socket.send 的返回值,由于 socket.send 返回实际发送的直接个数,可能会小于期望(在这里是 self.outbound_buffer.popleft())。

由于 socket.send 可能没有将数据发送完成就返回了,造成了消息丢失的情况。

本来想自己修的,看了一眼 Pikamaster 分支,已经修正了(一次保证将一个帧(Frame)发送完成):

pika/adapters/base_connection.pylink
1
2
3
4
5
6
7
8
9
10
11
12
13
def _handle_write(self):
"""Handle any outbound buffer writes that need to take place."""
bytes_written = 0
if self.outbound_buffer:
frame = self.outbound_buffer.popleft()
try:
self.socket.sendall(frame)
bytes_written = len(frame)
except socket.timeout:
raise
except socket.error as error:
return self._handle_error(error)
return bytes_written

呵呵……

解决

  1. 自行打补丁;
  2. 使用 git 上的 Pika: pip install git+https://github.com/pika/pika.git
  3. 不用 Pika, 换其它的,比如 Kombu

我现在不用 Pika 了,用 Kombu

PS: Pika 的发布也太不积极了,都怎么久了还不发新版本。

Workaround: Failed to import unicodedata in Sublime Text 2 under Windows

It’s weird, while importing some .pyd modules, like pyexpat, unicodedata in Windows version of Sublime Text 2, you will get ImportError, for example:

1
import unicodedata

Will result in:

1
Import Error: No module named unicodedata

However, since standard pyd modules are not missing, reside correctly in the same folder as sublime_text.exe, we can add that folder to sys.path in order to allow embedded python interpreter to load these modules:

1
sys.path.insert(os.path.dirname(sys.executable))

After that, you can import these standard python modules without pain, I hope it’s useful for Sublime Text 2 packages developers who met the same problem before.