Python 使用 MySQL 数据库

#!/bin/env python
# -*- coding: utf-8 -*-

import MySQLdb

def printRow(res):
    for prop in res:
        print prop,
    print

conn = MySQLdb.connect(host="localhost",user="root",passwd="root",db="employees",port=3306)
cur = conn.cursor()

# fetchone()
# 获取一条结果
print '='*5 + 'fetchone' + '='*5
rows = cur.execute("select count(*) from employees")
result = cur.fetchone()
print "rows:%d, result:%d" % (rows,result[0])

# fetchmany()
# 获取多条结果
print '='*5 + 'fetchmany' + '='*5
rows = cur.execute("select * from employees limit 10")
print "rows:%d" % rows
result = cur.fetchmany(5)
print "first 5:"
for res in result:
    printRow(res)
print "last 5:"
result = cur.fetchmany(5)
for res in result:
    for prop in res:
        print prop,
    print

# fetchall()
# 获取所有结果
print '='*5 + 'fetchall' + '='*5
rows = cur.execute("select * from employees limit 10")
print "rows:%d" % rows
result = cur.fetchall()
print "all 10:"
for res in result:
    printRow(res)

# scroll()
# 控制游标的移动
print '='*5 + 'scroll' + '='*5
rows = cur.execute("select * from employees limit 10")
print "rows:%d" % rows
print "first row:"
first_row = cur.fetchone()
printRow(first_row)
# absolute 从第 0 条位置向下移动 2 条
print "third row:"
cur.scroll(2,mode='absolute')
third_row = cur.fetchone()
printRow(third_row)
# relative 从当前移动 3 条
print "seventh row:"
cur.scroll(3,mode='relative')
seventh_row = cur.fetchone()
printRow(seventh_row)

# executemany()
# 用多个参数执行同一条语句,参数是所有支持迭代的对象
print '='*5 + 'executemany' + '='*5
genders = ['F','M']
rows = cur.executemany('select * from employees where gender=%s limit 1', genders)
result = cur.fetchall()
print rows
print result
# 执行结果表明每一条语句执行过后游标 cursor 都会改变。
# 因此不适合多参数的查询,而适合多参数的插入

# conn.commit()
# 用来执行事务
print '='*5 + 'commit' + '='*5
try:
    cur.execute("insert into employees values(0,date(now()),'Jack','Sparrow','M',date(now()))")
    cur.execute("insert into employees values(1,date(now()),'Jack','Sparrow','M',date(now()))")
    raise MySQLdb.Error
    conn.commit()
except MySQLdb.Error,e:
    # cur.execute("delete from employees where emp_no in (0,1)")
    print "Error Message: %s" % str(e.args)

cur.close()
conn.close()

SVN迁移到Git:保持提交记录

1.前言

最近组里要把之前在 SVN 上管理的项目迁移到 SVN 上去,所以花了一个晚上研究了一下 SVN 向 Git 迁移的方法。这里首先给出一种能保存提交记录的方案。

2.实施准备

再实施迁移之前,有几件事情是必须做的:

  1. 获取 SVN 路径权限
  2. 将本机的公钥提交到 Git 服务器上
  3. 在 Git 服务器上建立仓库

3.实施过程

实施的过程分为以下几个步骤:

  1. 将 SVN checkout 到本地
  2. 获取用户名列表
  3. 用 git svn clone 创建 Git 仓库
  4. 导出 .gitignore 文件
  5. 调整分支和标签
  6. 推送到远程

a. 将 SVN checkout 到本地

svn checkout --username USER_NAME --password PASSWORD https://svn.xxx/dir_name  dir_name

b. 获取用户名列表

通过如下命令获得用户列表:

svn log --xml | grep -P "^<author" | sort -u | perl -pe 's/<author>(.*?)<\/author>/$1 = /'

然后按照如下格式放入一个文件中(比如 user.txt):

schacon = Scott Chacon <schacon@geemail.com>
selse = Someo Nelse <selse@geemail.com>

c. 用 git svn clone 创建 Git 仓库

git svn clone --no-metadata -A users.txt https://svn.xxx/dir_name  dir_name

d. 导出 .gitignore 文件

git svn show-ignore > .gitignore
git add .gitignore
git commit -m 'Convert svn:ignore properties to .gitignore.'

e. 调整分支和标签

首先要移动标签,把它们从奇怪的远程分支变成实际的标签,然后把剩下的分支移动到本地。

cp -Rf .git/refs/remotes/tags/* .git/refs/tags/
rm -Rf .git/refs/remotes/tags

接下来,把 refs/remotes 下面剩下的索引变成本地分支:

cp -Rf .git/refs/remotes/* .git/refs/heads/
rm -Rf .git/refs/remotes

f. 推送到远程

git remote add origin git@my-git-server:myrepository.git
git push origin --all

4. Python 脚本

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import sys
import commands

if len(sys.argv) < 3:
    print 'Usage: python %s USERNAME PASSWORD' % sys.argv[0]
    exit(255)

user_name = sys.argv[1]
user_pwd  = sys.argv[2]

svn_repos = [
        svn_path,
        ......
        ]

git_repos = [
        git_path,
        ......
        ]

users = {}

def init():
    run_cmd('.', 'mkdir git')
    run_cmd('.', 'mkdir svn')

def run_cmd(dir_name, cmd):
    cmd_str = 'cd %s && %s' % (dir_name, cmd)
    print cmd_str
    status,output = commands.getstatusoutput(cmd_str)
    print 'status: %d' % status
    print 'output: \n%s' % output
    print '=========================================================\n'

    return status,output

def clone_svn_to_local(svn_addr):
    repo_dir_name = get_repo_dir_name(svn_addr)
    run_cmd('svn', "svn checkout --username %s --password %s %s %s" % (user_name, user_pwd, svn_addr, repo_dir_name))
    _,output = run_cmd('svn/' + repo_dir_name, "svn log --xml | grep -P '^<author' | sort -u | perl -pe 's/<author>(.*?)<\/author>/$1 = /'")
    lines = output.split("\n")
    for line in lines:
        name = line.split('=')[0].strip()
        users[name] = ('%s = %s <%s@didichuxing.com>' % (name, name, name))

def write_users():
    with open('git/users.txt', 'w+') as f:
        for name in users.values():
            f.write('%s\n' % name)

def git_svn_clone(svn_addr):
    repo_dir_name = get_repo_dir_name(svn_addr)
    run_cmd('git', 'git svn clone --no-metadata -A users.txt %s %s' % (svn_addr, repo_dir_name))
    run_cmd('git/' + repo_dir_name, 'git svn show-ignore > .gitignore')
    run_cmd('git/' + repo_dir_name, 'git add .gitignore')
    run_cmd('git/' + repo_dir_name, "git commit -m 'Convert svn:ignore properties to .gitignore.'")
    run_cmd('git/' + repo_dir_name, 'cp -Rf .git/refs/remotes/tags/* .git/refs/tags/')
    run_cmd('git/' + repo_dir_name, 'rm -Rf .git/refs/remotes/tags')
    run_cmd('git/' + repo_dir_name, 'cp -Rf .git/refs/remotes/* .git/refs/heads/')
    run_cmd('git/' + repo_dir_name, 'rm -Rf .git/refs/remotes')

def diff_git_and_svn_repos(svn_addr):
    repo_dir_name = get_repo_dir_name(svn_addr)
    git_repo_path = 'git/' + repo_dir_name
    svn_repo_path = 'svn/' + repo_dir_name
    _,output = run_cmd('.', 'diff -r %s %s' % (git_repo_path, svn_repo_path))
    expected = 'Only in git/%s: .git\nOnly in git/%s: .gitignore\nOnly in svn/%s: .svn' % (repo_dir_name, repo_dir_name, repo_dir_name)
    if expected != output:
        raise Exception('迁移后内容不一致:\n%s' % output)

def push_to_remote(svn_addr, git_addr):
    repo_dir_name = get_repo_dir_name(svn_addr)
    run_cmd('git/' + repo_dir_name, 'git checkout -b svn_migrate_branch')
    run_cmd('git/' + repo_dir_name, 'git remote add origin %s' % git_addr)
    run_cmd('git/' + repo_dir_name, 'git push origin --all')

def get_repo_dir_name(svn_addr):
    return svn_addr.split('/')[-1]

def main():
    init()

    for svn_addr in svn_repos:
        clone_svn_to_local(svn_addr)

    write_users()

    repo_nums = len(git_repos)
    for i in range(0,repo_nums):
        git_svn_clone(svn_repos[i])
        diff_git_and_svn_repos(svn_repos[i])
        push_to_remote(svn_repos[i], git_repos[i])

if __name__ == '__main__':
    main()

4. 参考文献

5. 一点想法

其实大家都能看出来这个脚本大部分都是对 shell 命令的调用。为什么舍近求远呢?
因为本人一直觉得 shell 的内部命令,关于流程控制以及字符串处理等实在不好用。我更倾向于 Python 和 Ruby 这样强大的脚本语言。
其实我对 Python 用的也不是很多,在写脚本的过程中查阅了一些 Python 调用 shell 的资料,也和 Ruby 中类似的代码做个比较。
在上面的脚本中我使用了 commands 包的 getstatusoutput('cmd') 方法 。该函数返回了一个元组,通过序列解包可以同时获得返回值和标准输出流的输出。一般调用方法为:

(status, output) = commands.getstatusoutput('shell 命令')
print status, output

而 Ruby 中也有类似的方法,即 Kernel 模块中的 `cmd` 方法。但是该方法直接返回的只有标准输出,返回值还要靠 $? 来获取。

irb(main):014:0> `echo hello && exit 99`
=> "hello\n"
irb(main):015:0> $?.exitstatus
=> 99

在便利性上,Python 的序列解包使得多个函数返回值变成可能,减少了封装也提高了使用的便利性。

使用 Quartz API

What is Quartz?

Quartz 是一个功能丰富的开源作业调度类库,可以与各种规模的 Java 应用集成。

Instantiating the Scheduler

SchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
scheduler.start();
JobDetail printJob = JobBuilder.newJob(PrintJob.class)
  .withIdentity("print job", "default group").build();

Trigger trigger = TriggerBuilder.newTrigger().
  withIdentity("print trigger", "default group").startNow().
  withSchedule(SimpleScheduleBuilder.simpleSchedule().
      withIntervalInSeconds(2).
      repeatForever()).build();

scheduler.scheduleJob(printJob,trigger);

Key Interfaces

接口名称 说明
Scheduler 与调度器交互的主要API
Job 调度器执行的组件需要实现的接口
JobDetail 用来定义Job的实例
Trigger 定义给定Job何时执行的组件
JobBuilder 用来定义/构建JobDetail实例
TriggerBuilder 用来定义/构建触发器实例

各种 Builder:

  • org.quartz.JobBuilder
  • org.quartz.SimpleScheduleBuilder
  • org.quartz.CronScheduleBuilder
  • org.quartz.CalenderIntervalScheduleBuilder
  • org.quartz.TriggerBuilder
  • org.quartz.DateBuilder

Jobs and Triggers

一个作业是一个实现 Job 接口的类,该接口只有一个方法:

package org.quartz;
public interface Job {
  public void execute(JobExecutionContext context)
    throws JobExecutionException;
}

当作业的触发器启动,execute 方法被调度器的某个工作线程(worker thread)调用。JobExecutionContext 对象被传递给方法以提供作业实例所在运行时环境的的信息,包括调度器和触发器的句柄、作业的 JobDetail 对象以及其他一些条目。

JobDetail 对象由你的程序在将作业添加到调度器时创建。这个对象包含作业的各种属性设定信息,包括一个被用来存储给定 Job 类实例状态信息的 JobDataMap。

Trigger 对象被用来触发作业的执行。触发器也可以有一个关联到它们的 JobDataMap。JobDataMap 在把作业参数传递给触发器的执行时是很有用的。Quartz 提供一系列不同的触发器类型,最常用的是两个:

  • SimpleTrigger。如果需要你需要“一次性”调用或者在某个给定时间每隔一个时间段 T 触发 N 次,你会觉得它很顺手。
  • CronTrigger。如果你希望像基于日历一样调度作业,它很有用。

Quartz 将作业和触发器分开的设计有很多好处。例如:你可以创建作业并将它们存储在作业调度器中而不关联触发器。这使你能够将许多触发器关联到同一个作业。这种松耦合的另一个好处是能够配置关联触发器已经失效但是仍然驻留在调度器中的作业。这使你能够在随后再次调度它们而不需要重新定义它们。这种设计还允许你在不需要重新定义关联作业的前提下修改和替换触发器。

Identities

作业和触发器在 Quartz 调度器注册时被给予标识键。作业和触发器的键(JobKey 和 TriggerKey)允许它们被放置在组中,便于组织作业和触发器。作业和触发器的键的名字部分必须是组内唯一的。换句话说,作业或者触发器完整的键(或者标识符)由名字和组组成。

一个跨平台的离线文档浏览器——Zeal

引言

对于一个程序员来说,查阅编程语言或者框架的手册是家常便饭,但是频繁打开浏览器总是一件很麻烦的事情。

之前在学 Ruby on Rails 时看到了 MacBook 上的 Dash,用起来真方便!可惜也只是 MacBook 上的,我等穷鬼只能羡慕。

小小搜索了一下,在 Github 上发现了一个名为 Zeal 的项目。

Zeal

这个软件的有以下几个特点:

  1. 跨平台。Windows 和 Linux 下都有对应版本。
  2. 支持众多的框架和语言。听过的没听过的都能在这里找到,还可以定制自己的语言手册。
  3. 与集成开发环境和文本编辑器集成。
  4. 支持搜索。

放一张图片:
示例图

更多资料