Python实现数据类的方法集合

当前Python最新稳定版为3.6。主流Linux发行版官方仓库提供Python版本的有2.7和3.5,默认依然是坚强的2.7。

1、dataclass装饰器
正规军,需要Python 3.7+,还在路上。
dataclass装饰器会给类添加__init__、__str__、__repr__等方法。

@dataclass
class InventoryItem:
    '''Class for keeping track of an item in inventory.'''
    name: str
    unit_price: float
    quantity_on_hand: int = 0
 
    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

2、collections.namedtupletyping.NamedTuple
要求不多的用这两个最合适了。Python 3.6+的typing.NamedTuple才支持默认参数。

class Employee(NamedTuple):
    name: str
    id: int = 3
 
employee = Employee('Guido')
assert employee.id == 3
Employee = namedtuple('Employee', 'name, age, title, department, paygrade')

3、autoassign装饰器,和auto_assign装饰器。
算是奇技淫巧,前者支持Python2,后者支持Python 3.5+。

4、collections.namedtuple + functools.partial。
通过partial实现默认参数,也是我当前采用的方案。

fields = ("name", "age", "title")
defaults = OrderedDict({"title": None})
Employee = partial(namedtuple("Employee", fields), **defaults)
employee = Employee("Guido", "18")

网上还有一些相对麻烦点的方案,比如重写namedtuple的__new__方法,就不一一列举了

Posted in Uncategorised | Leave a comment

在Linux Minecraft中输入中文——进阶:在告示牌输入中文

两年前的一篇文章,我讲到如何在Linux Minecraft中输入中文。脚本内容是转的别人的。该方法只能用于聊天,不能用于编辑告示牌。

其实只要把脚本修改下,就可以通用。只会复制粘贴,不会派生定制,做程序员的我真的不好意思。

原脚本内容为:

#!/bin/bash -e
 
chars=$(zenity --title 中文输入 --text 中文输入 --width 500 --entry 2>/dev/null)
sleep 0.1
xdotool key --delay 150 Escape t
sleep 0.2
xdotool type --delay 150 "$chars"
xdotool key Return

其中正文第一行为弹出对话框,接收用户输入。第三行输出t键。第五行输出对话框接收的输入。第六行输出回车。很明显,第一行和第五行才是重点。其它几行不过辅助性的,帮用户偷懒。

现在只要改下:

#!/bin/bash -e
 
chars=$(zenity --title 中文输入 --text 中文输入 --width 500 --entry 2>/dev/null)
#sleep 0.1
#xdotool key --delay 150 Escape t
sleep 0.2
xdotool type --delay 150 "$chars"
#xdotool key Return

只要注释掉辅助性的几行。

编辑告示牌时,通过快捷键执行该脚本,即可在告示牌上输入中文。如果是聊天,得先输入t打开聊天栏,再通过快捷键执行该脚本,聊天编辑栏得到中文后,敲回车——或者新脚本和旧脚本共存,绑定到不同的快捷键。

以前我在游戏中结识位朋友。他发现另一个在告示牌输入中文的办法。可惜我还没学会,就与他失去了联系。

Posted in Uncategorised | Tagged | Leave a comment

一次网件路由器OpenWrt系统TFTP刷机抢救记录

路由器网件WNDR4300,原固件为OpenWrt15.05,计划升级到OpenWrt15.05.1。下载了wndr4300-squashfs-sysupgrade.tar,通过LuCI升级。确认升级前,检查升级文件摘要匹配。
路由器重启后,不能获得IP地址,能PING通网关。不能访问luCI,SSH连接网关密码始终无效。路由器在开机后,指示灯只亮电源指示灯和有连接的LAN指示灯。
开始TFTP升级。先下载固件镜像wndr4300-ubi-factory.img。将电脑通过网线连接到路由器LAN接口。电脑网络地址改为手动设置,IP地址为192.168.1.2-254,网关192.168.1.1,子网掩码255.255.255。ping 192.168.1.1,确保可以连接到路由器。
路由器关机,按下RESET键,再开机。先指示灯全亮。等待电源指示灯由黄灯常亮变为黄灯闪烁,再变为绿灯常亮,最后变为绿灯闪烁。路由器进入TFTP刷机模式。松开RESET键。利用TFTP工具将固件镜像上传到路由器。如果是使用Linux的tftp软件上传,需要设置模式为binary。
等待LuCI可以访问,设置ROOT密码,恢复备份存档——前提是你升级前备份了存档!
如果tftp救不了你的机器,准备TTL刷机吧。

Posted in Uncategorised | Tagged | Leave a comment

长沙寻访古旧书店(一)

首先说明,本次寻访日期为2018年1月9日。本次寻访大区域为芙蓉区,寻访依据以网友提供的线索为主。

第一块区域为湘雅附二医院一带。该区域也靠近湖南图书馆。人民路北侧,湘雅附二医院以东,有三家旧书店。三家书店都是小门面,提供二手旧书。我淘到一本人民出版社的《国际共产主义运动史》,1978年版。向老板询价,老板示意书背面。翻过来看,价格在背面用铅笔标注了,13元。

结账时,又向老板打听附近可有其它旧书店,老板告知复兴街还有家。复兴街其实只能算巷子。入口位于附二医院西边。人民路附二医院西边的口子进去不远,可以看到一家“复兴参观”,进入就是。复兴街北接白沙路。穿过林立的小餐馆,进入相对安静的居民区,就可看到一家孤立的旧书店。该店比之前三家店面稍大,卖的也都是旧书。

既然来到白沙路附近,就免不得去天心阁一带。简牍博物馆下面古玩商店不少,然而没有旧书店。询问了老板,才知道要等到周末才有旧书摊。只好作罢。

下一站八一路。从简牍博物馆到八一路,刚好路过定王台,免不得要过去寻访一番。这次没找到定王台书市对面的述古人文书店。发现了一间民生书局。民生书局卖的是出版社库存书。库存书又免不了堆积大量湖南本地出版社的库存书。淘到一本湖南教育出版社的《青藏高原科考访谈录》,2010年版。店家要价25。这次价格没有用铅笔在书背标注。见书品相较好,价格实惠,就立马掏了钱。

IMG_20180109_134850

出了民生书局,再去搜寻述古人文书店,才发现述古已经关了门,招牌上贴上了招租的广告。

最后一站是八一路的星星书店。星星书店位置是袁家岭地铁站北面。大致占据了两个半的小门面。书店以出版社库存书为主。同前面的民生书店,星星书店也不少湖南本地出版社的书,库存书又以湖南科学技术出版社的书为主。淘到《爱因斯坦传》、《西方科学起源》、《居里一家》,三本都是湖南科学技术出版社的书,而且都是全新的,前两本还有原书塑封。店里上豆瓣搜了下,前两本评论都蛮高,后一本评价人数太少,但看介绍也不错,想必不会浪费票子。又搜到一本港版袁伟时的《文化:中国与世界》,木刻文化2010年版,扉页还有作者的签名“丁总赐正 袁伟时 2011.4.27”。丁总显然工作太忙,无暇“赐正”。书一页没翻,就流落到旧书市场。

IMG_20180109_161608

结账时算收银员价格怎么算。收银员告知三本湖南科学技术出版社的书算四折,一本中华书局的《老子注译及评介》算六折,港版的《文化:中国与世界》打了个电话请示后表示30元。果断放弃了《老子》。买单时掏出钱包所有零钱,刚好还差三毛,问能否优惠三毛。收银美女很直接地答应了。付了钱出门时,收银美女还很客气地道了声慢走。(可惜了,我习惯了没礼貌,不知道怎么回)

IMG_20180109_143946

于是满载而归,背着沉重的背包去袁家岭搭地铁回家。

Posted in Uncategorised | Leave a comment

Debian stretch安装nvidia显卡驱动,并配置双显卡切换

机器是戴尔老爷机,Dell inspiron 14r 7420,显卡为Intel + Nvidia。

之前Debian wheezy上双显卡解决方案见我的这篇。建议先阅读该篇。

下面是我解决问题之后的简单总结。实际摸索过程远比这复杂。各人机器环境不同,下面的方案绝非放之四海皆准——仅供参考!

第一步,为默认源添加contrib non-free组件,并更新本地仓库缓存。

第二步,安装nvidia-driver驱动:

sudo apt-get install linux-headers-$(uname -r|sed ‘s,[^-]*-[^-]*-,,’) nvidia-driver

不要执行nvidia-xconfig!

第三步,安装bumblebee-nvidia,支持双显卡切换:

sudo apt-get install bumblebee-nvidia primus

添加当前用户到bumblebee用户组:

sudo adduser $USER bumblebee

第四步,重启

 

如果遇到问题,检查/var/log/Xorg.0.log、/var/log/user.log

参考:

NvidiaGraphicsDrivers – Debian Wiki

Bumblebee – Debian Wiki

双显卡机器安装Debian Wheezy运行Steam游戏

Posted in Uncategorised | Tagged , , | Leave a comment

遭遇MongoDB勒索

最近测试环境表现得不正常,MongoDB的数据展现不出来。
查了日志,发现有这么几条:

2017-02-08T23:28:32.384+0000 I COMMAND [conn773] dropDatabase PLEASE_READ_ME starting
2017-02-08T23:28:32.388+0000 I COMMAND [conn773] dropDatabase PLEASE_READ_ME finished
2017-02-08T23:28:34.581+0000 I COMMAND [conn773] dropDatabase iwesee starting
2017-02-08T23:28:34.601+0000 I COMMAND [conn773] dropDatabase iwesee finished

我的代码中不可能有dropDatabase的操作,也没这么手动操作过。

既然PLEASE_READ_ME,那我去就读。
在数据库中发现了PLEASE_READ_ME库,库下有PLEASE_READ_ME集合,集合中有这么个文档:

{ 
    "_id" : ObjectId("58a06f3a6d3d693e86ae59da"), 
    "info" : "Don't panic. Your DB is in safety and backed up (check logs). To restore send 0.05 BTC and email with your server ip or domain name. Each 48 hours we erase all data.", 
    "amount" : "0.05 BTC", 
    "data_we_have" : {
        "DB_H4CK3D" : [
            "URG3NT_W4RN1NG"
        ], 
        "iwesee" : [
            "ticket.subject.movie.nowplaying", 
            "ticket.subject.movie.later", 
            "ticket.subject.photo", 
            "ticket.subject"
        ]
    }, 
    "Bitcoin Address" : "16bm9fMWdmGCmhXDm3QqMgzwAFwPzLpGaR", 
    "email" : "kraken8888@sigaint.org"
}

日志中还有不少国外的ip连接记录。

于是确认自己遇上了传说中的数据库勒索者了。
0.05BTC我是没有。
数据库也不过是测试数据库,删除了还能自动重建。当初为了方便访问,才绑定公网地址并没设密码。
于是重建mongo容器,改为绑定内网地址,并趁这个机会,升级到3.4。以后访问,也只好加SSH通道。

Posted in Uncategorized | Tagged | 1 Comment

实现golang程序热升级

简单定义热升级就是:让运行中的服务改为执行新的程序代码逻辑,且不中断服务。

目前现有的开源热升级程序主要有endless,和beego的grace组件。

经过测试,grace在热升级时,会掐掉当前处理中的连接,自然无法接受。endless的问题在于每次热升级都是创建子进程后,原进程就退出了,这点显然不符合守护进程的要求。

于是自己实现个新的。逻辑参考了endless。其实endless和grace也是相互“借鉴”,相互copy了很多代码。我在“借鉴”时,力求逻辑尽可能简单,自己编写全部代码,并后续完善一些关键细节。

首先要解决多个进程监听同一地址+端口。我首先想到的是nginx、node.js的端口复用方案。但golang尚不支持端口复用。好在golang支持文件描述符和listener的互转,和文件描述符的继承。方案为主进程创建listener,取得文件描述符,子进程再继承listener文件描述符。

新进程应该执行新程序的逻辑。要实现这点,就不能用fork,不然新进程还是执行旧的程序逻辑。

支持守护进程。这点是我重新实现热升级的关键。endless和grace的逻辑是,创建子进程,然后原进程退出。我的方案是一个master进程,一个worker进程。每次只升级worker进程。

master进程与worker进程在外部看起来应该是一体的。这里沿袭了erlang任其崩溃的思想,一个退出,另一个跟随。如果worker进程退出码非0,master进程以相同的退出码结束。

还有很重要的一点,退出worker进程,首先关闭listener,一并关闭listener关联的文件描述符,再等待所有连接关闭。

支持自动升级。这个不是很必要,以后再说。

相比endless,我的实现的另一个优势是:只提供listener接口,而不是ListenAndServe函数。也就是该实现不仅支持http/https,同时支持其它基于tcp的应用层协议,比如grpc。

Posted in Uncategorized | Tagged | 3 Comments

自定义tornado日志格式

第一次玩tornado。版本4.x。为了解决日志格式的问题,google了很多,没一个有效的。

tornado日志格式分两块,一块是logging的格式,一块是tornado请求消息格式。

tornado默认的访问日志输出是这样的:
WARNING:tornado.access:404 GET / (127.0.0.1) 167.93ms
其中,WARNING:tornado.access:404为logger日志内容,GET / (127.0.0.1) 167.93ms为访问消息。

根据enable_pretty_logging函数,可知tornado默认采用logging的root logger,并且,如果logger没有配置handler,则为其添加一个默认的StreamHandler。
因而,应该为logging root logger预创建Handler,并指定日志格式。

依据tornado文档,输出访问日志的为Application.log_request,如果要自定义访问日志输出,可以继承Application并重写log_request,或者自定义log_request函数,并设置为Application.settings的log_function属性。
当然选择后者。log_function函数的写法,参照原版Application.log_request就行。

最后贴上我的代码供Google到此的朋友参考:

import logging
import tornado.log
 
# 日志格式
logging.getLogger().setLevel(logging.INFO)
formatter = logging.Formatter(fmt="%(levelname).4s %(asctime)s %(name)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logging.getLogger().addHandler(handler)
 
# 日志消息格式
def log_request(handler):
    if handler.get_status() == 400:
        log_method = tornado.log.access_log.info
    elif handler.get_status() == 500:
        log_method = tornado.log.access_log.warning
    else:
        log_method = tornado.log.access_log.error
 
    request_time = 1000.0 * handler.request.request_time()
    log_method("%d %s %.2fms %s", handler.get_status(), handler._request_summary(), request_time, handler.request.headers.get("User-Agent", ""))
 
# 自定义tornado日志消息格式
app.settings["log_function"] = log.log_request
Posted in Uncategorized | Tagged , | Leave a comment

docker挂载文件的更新问题

将宿主机配置文件挂载到容器,在宿主机修改配置文件,但容器内挂载的文件并未发生变化,直至docker重启。

记得不是这样的。起初以为是版本的原因。于是在最新版docker上做了测试,问题依旧。

只好Google之。最后从docker issue上找到了答案:
docker挂载文件基于inode。vim等编辑工具保存文件时,并非直接保存,而是将一份新的临时文件覆盖了旧文件。对于inode而言,原文件并未被修改。

解决方案有三:
1、换用nano等直接更新文件的编辑工具
2、改为挂载目录。
3、修改vim配置,添加:set backupcopy=yes

参考:
https://github.com/docker/docker/issues/15793
http://www.ruanyifeng.com/blog/2011/12/inode.html

Posted in Uncategorized | Tagged | Leave a comment

wordpress启用全站加密

最近切换到vultr的洛杉矶机房,放弃了之前的cloudflare加速,启用Let’s Encrypt加密。
Let’s Encrypt证书的获取和更新见这里,完整文档见这里

先讲获取和更新证书。具体的命令见官方文档。我这里只讲官方没讲清楚或需要注意的地方。
1、安装certbot。
2、获取证书。因为是nginx代理,必须webroot插件。webroot使得获取和更新证书无须停掉web服务。使用webroot需要指定一个目录存放webroot的临时文件,且nginx需要配置一个模式为.well-known/acme-challenge的location指定到该目录。
3、保持更新

再配置wordpress和nginx
1、修改wordpress常规配置中的两个地址到https。这点,直接修改数据库也可以做到。
2、启用后台加密。修改wp-config.php文件,添加define(‘FORCE_SSL_ADMIN’, true);定义。
3、最后是最关键的,配置nginx,将http重定向到https,将https转发到wordpress。

下面是我的配置:

server {
     listen         80;
     server_name    blog.wencan.org;
     return         301 https://$server_name$request_uri;
}
 
server {
    listen       443 ssl;
    server_name  blog.wencan.org;
 
    ssl on;
    ssl_certificate /etc/letsencrypt/live/blog.wencan.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/blog.wencan.org/privkey.pem;
 
    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;
 
    location / {
	proxy_pass http://127.0.0.1:81/;
        proxy_redirect      off;
        proxy_set_header    Host            $host;
        proxy_set_header    X-Real-IP       $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_set_header    X-Forwarded-Proto $scheme;
    }
 
    location /.well-known/acme-challenge {
        root /etc/nginx/webroot;
    }
}

关键是X-Forwarded-Proto头的配置。该location的其它内容都算是标配,http代理的标配。但这里得配置X-Forwarded-Proto头,告诉wordpress,前端是https代理!否则,网页很多链接,依然是顽固的http链接。

Posted in Uncategorised, Uncategorized | Tagged , | 3 Comments