Feb 28, 2019 - jekyll简明使用说明

  1. 什么是jekyll? jekyll是一个简单的免费的Blog生成工具(将纯文本转化为静态网站和博客),类似WordPress。但是和WordPress又有很大的不同,原因是jekyll只是一个生成静态网页的工具,不需要数据库支持。但是可以配合第三方服务,例如Disqus。最关键的是jekyll可以免费部署在Github上,而且可以绑定自己的域名。我们可以在客户端生成静态网站,然后上传到服务器。

  2. 安装
    • 下载windows安装包,参考http://www.madhur.co.in/blog/2013/07/20/buildportablejekyll.html 软件包位于:https://github.com/madhur/PortableJekyll ,已经包括git包
    • git clone https://github.com/scotte/jekyll-clean.git #下载主题
    • gem isntall bundler #更新bunller
    • bundle install #安装依赖
    • 启动 cd jekyll-clean jekyll server # 启动本地 http://127.0.0.1/jekyll-clean ( jekyll serve –watch) jekyll build #重新生成静态网站
  3. 上传到git
    • 新建github pages
    • git clone git@github.com:yourname/yourname.github.io.git #复制github
    • 下载jekyll模板,并安装上面的步骤本地测试
    • 将下载的模板copy到yourname.github.io目录,注意不要覆盖.git目录
    • 修改_config.yml url: https://scotte.github.io/jekyll-clean 改为https://breezecloud.github.io/ baseurl: /jekyll-clean 改为 ‘‘(根目录)
    • 上传到服务器 git add . git commit -a -m “版本说明” git push
    • jekyll serve 浏览https://breezecloud.github.io/
    • 设置域名,如果你想设置自己的域名,在breezecloud.github.io的目录下有一个CNAME目录,将自己的域名写如(比如:youery.cn),同时设置域名解析服务器增加记录(@表示直接解析)
  4. 添加文章 _post目录下增加md文件,增加头信息如:
    ---
    layout: post
    title:  "给树莓派安装ArchLinux"
    date: 2017-12-15 16:25:06 -0700
    ---
    

    生成的静态网站会自动根据时间排序,之后执行jekyll build重新生成site
    参考:
    https://www.jianshu.com/p/9f71e260925d
    https://www.jekyll.com.cn

Feb 20, 2019 - 微信公众号后台程序的Django版本实现

“微信公众号后台程序的Django版本实现”

  1. 为何要写Django版本的微信后台程序   官方已经提供了详细的帮助文档和开发教程。关于微信后台如何工作,请仔细阅读官方帮助文档,本文档不会详细介绍。为什么还要写?当你阅读了开发者文档的入门指南,迫不及待的要用教程上面的例程运行时,发现上面的关键软件包web.py只支持python2(github上有pythhon3的版本,但试了没成功),而你的其它包很可能要用python3。首先python2已经不再推荐了,更糟糕的是当你想去找找有没有最新版本的web.py能支持python3的时候,发现web.py已经停止更新了(开发者已经狗带)。无奈之下心里默默的诅咒了一下腾讯,这个教程估计几年也不更新了。后来想是不是可以自己用Django框架写一个后台程序,这样可以使用Django的全部特性。本着不要重复造轮子的精神,将程序贴出来供有需要的程序猿参考,少走点弯路。

  2. 手机和微信公众号交互信息的流程是什么?   当我们向微信公众号发送一条信息时,到底发生了什么?大致流程是手机发出的信息被腾讯的后台服务器收到,然后经过处理之后发到我们事先设置的服务器上;我们的服务器经过处理之后在发送回腾讯的服务器,腾讯服务器再将我们服务器发送的消息发回手机端。其中我们的服务器和腾讯的服务器中间走的是http协议(还必须是80端口,这点挺蛋疼的),这就是我们需要处理的部分,其余流程是腾讯自己的事情。因为中间是http协议,所以理论上任何web框架均可以作为开发程序。

  3. 最小的Django程序 一般教程在写Django程序时需要有很多的配置文件,我们的目标只是写一个微信后台的Demo程序,所以不需要那么多的配置文件,可以用一个最小的Django程序开始,至于Django的相关介绍请自行学习。这里直接上代码: ```

    -- coding: utf-8 --

    filename: main.py

    #微信公众平台验证程序,参考微信mp平台文档从web.py修改为django import sys from handle import Handle from django.conf import settings

settings.configure( DEBUG=True, ALLOWED_HOSTS=[‘118.89.144.1’], # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = ‘s&!9ejh-hbr5op@z@2^_k!6uhy)ykm)gv=4+9&8c9gld0@q&ea’, ROOT_URLCONF = name, MIDDLEWARE_CLASSES = ( ‘django.middleware.common.CommonMiddleware’, ‘django.middleware.csrf.CsrfViewMiddleware’, ‘django.middleware.clickjacking.XFrameOptionsMiddleware’, ) )

from django.conf.urls import url from django.http import HttpResponse

def index(request): MyHandle = Handle() return HttpResponse(MyHandle.GET(request))

urlpatterns = ( url(r’^wx/$’, index), )

if name== ‘main’: from django.core.management import execute_from_command_line execute_from_command_line(sys.argv)

以上的程序,只要再完成Handle(),就可以在命令行下执行python main.py runserver 0.0.0.0:80 启动。Handle程序见下面4。

4. 设置我们的服务器
  实现后台微信公众号程序,我们必须有一台公网的服务器,而且程序还必须使用80端口(腾讯要求)。当你在进入腾讯设置页面设置自己的服务器时,会碰到token的概念。什么是token?token:只用于验证开发者服务器。也就是token不参与消息发送接收过程,在微信管理平台的“开发-基本配置”页面,可以设置开发者服务器的地址,但在修改时微信需要验证一下服务器(但我没看出来有什么必要),这时需要开发者服务器能响应微信管理平台的验证请求,token是微信和开发者服务器之间的一个约定。
  所以,当你要设置服务器地址时,需要一个验证程序来证明这个服务器是你设置的,验证程序的详细说明参考官方文档的“入门指引”,这里直接列出Djiano版本的源代码以取代官方文档中“1.4开发者基本配置”的程序。

-- coding: utf-8 --

filename: handle.py

import hashlib class Handle(object): def GET(self,request): try: #微信认证应该只支持GET方式 if request.method == ‘GET’: #print(‘request.GET=%s’%request.META[‘QUERY_STRING’]) signature = request.GET.get(‘signature’) signature = signature.encode(‘utf-8’) #默认是utf-8的字符,转换为bytes,下同 #print(“signature=%s”% signature) timestamp = request.GET.get(‘timestamp’) timestamp = timestamp.encode(‘utf-8’) #print(“timestamp=%s”% timestamp) nonce = request.GET.get(‘nonce’) nonce = nonce.encode(‘utf-8’) #print(“nonce=%s”% nonce) echostr = request.GET.get(‘echostr’) token = “xxxx” #请按照公众平台官网\基本配置中信息填写 token = token.encode(‘utf-8’) mylist = [token, timestamp, nonce] mylist.sort() sha1 = hashlib.sha1() list(map(sha1.update, mylist)) #python3的map用法和python2不同,map返回object hashcode = sha1.hexdigest() hashcode = hashcode.encode(‘utf-8’) #print(“handle/GET func: hashcode=%s, signature=%s “%( hashcode, signature)) if hashcode == signature: return echostr else: return “check error” except Exception as e: #print(e) return e

5. 最简单的微信公众号完整后台程序
  有了以上的基础就可以写一个最简单的微信公众号完整后台程序,该程序在粉丝发送任意消息给公众号之后,回复“测试”两个字。程序共4个文件:main.py程序同上面3;handle.py改成下面的程序;reply.py和receive.py同教程里面“2.5 码代码”的程序,这里不再重复。

-- coding: utf-8 --

filename: handle.py

import hashlib import reply import receive from django.utils.encoding import smart_bytes

class Handle(object): def POST(self,request): try: #粉丝的信息用POST方式发送给自己服务器 if request.method == ‘POST’: webData = smart_bytes(request.body) #此处是关键,获得http头信息,并转换为字符串(去unicode),参考自强学堂 print(“Handle Post webdata is %s”% webData) #后台打日志 recMsg = receive.parse_xml(webData) if isinstance(recMsg, receive.Msg) and recMsg.MsgType == ‘text’: toUser = recMsg.FromUserName fromUser = recMsg.ToUserName content = “测试” replyMsg = reply.TextMsg(toUser, fromUser, content) return replyMsg.send() else: print(“暂且不处理”) return “success” except Exception as e: print(e) return e ```

  1. 后续   如果能完成上面的测试,恭喜你已经掌握了微信公众号开发后台程序的精髓,微信还支持图片、声音等多媒体信息,你可以结合Django的强大功能自由发挥了。等等,还有什么access_token?哦,忘了告诉你,如果要调用微信平台接口,你必须还要处理access_token(access_token的官方解释:公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。)。这个等以后有机会继续介绍吧,至少你今天已经有收获了。本文也作为微信公众平台开发文档的一个补充,建议读者还是仔细阅读微信的官方文档。

Jan 3, 2019 - 使用C或C++扩展Python(五)-定义扩展类()

原文:https://docs.python.org/3/extending/newtypes_tutorial.html

“使用C或C++扩展Python(五)-定义扩展类()”

``` /* custom3.c compile:gcc -fPIC -shared -I /usr/include/python3.5m/ custom3.c -o custom3.so usage:

import custom3 my = custom3.Custom() my.first = 1 Traceback (most recent call last): File “", line 1, in TypeError: The first attribute value must be a string del my.first Traceback (most recent call last): File "", line 1, in TypeError: Cannot delete the first attribute

reference:https://docs.python.org/3/extending/newtypes_tutorial.html */ #include #include "structmember.h"

typedef struct { PyObject_HEAD PyObject first; / first name / PyObject *last; / last name */ int number; } CustomObject;

static void Custom_dealloc(CustomObject *self) { Py_XDECREF(self->first); Py_XDECREF(self->last); Py_TYPE(self)->tp_free((PyObject *) self); }

static PyObject * Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { CustomObject *self; self = (CustomObject *) type->tp_alloc(type, 0); if (self != NULL) { self->first = PyUnicode_FromString(“”); if (self->first == NULL) { Py_DECREF(self); return NULL; } self->last = PyUnicode_FromString(“”); if (self->last == NULL) { Py_DECREF(self); return NULL; } self->number = 0; } return (PyObject *) self; }

static int Custom_init(CustomObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {“first”, “last”, “number”, NULL}; PyObject *first = NULL, *last = NULL, *tmp;

if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,
                                 &first, &last,
                                 &self->number))
    return -1;

if (first) {
    tmp = self->first;
    Py_INCREF(first);
    self->first = first;
    Py_DECREF(tmp);
}
if (last) {
    tmp = self->last;
    Py_INCREF(last);
    self->last = last;
    Py_DECREF(tmp);
}
return 0; }

static PyMemberDef Custom_members[] = { {“number”, T_INT, offsetof(CustomObject, number), 0, “custom number”}, {NULL} /* Sentinel */ };

static PyObject * Custom_getfirst(CustomObject *self, void *closure) { Py_INCREF(self->first); return self->first; }

static int Custom_setfirst(CustomObject *self, PyObject *value, void *closure) { PyObject *tmp; if (value == NULL) { PyErr_SetString(PyExc_TypeError, “Cannot delete the first attribute”); return -1; } if (!PyUnicode_Check(value)) { PyErr_SetString(PyExc_TypeError, “The first attribute value must be a string”); return -1; } tmp = self->first; Py_INCREF(value); self->first = value; Py_DECREF(tmp); return 0; }

static PyObject * Custom_getlast(CustomObject *self, void *closure) { Py_INCREF(self->last); return self->last; }

static int Custom_setlast(CustomObject *self, PyObject *value, void *closure) { PyObject *tmp; if (value == NULL) { PyErr_SetString(PyExc_TypeError, “Cannot delete the last attribute”); return -1; } if (!PyUnicode_Check(value)) { PyErr_SetString(PyExc_TypeError, “The last attribute value must be a string”); return -1; } tmp = self->last; Py_INCREF(value); self->last = value; Py_DECREF(tmp); return 0; }

static PyGetSetDef Custom_getsetters[] = { {“first”, (getter) Custom_getfirst, (setter) Custom_setfirst, “first name”, NULL}, {“last”, (getter) Custom_getlast, (setter) Custom_setlast, “last name”, NULL}, {NULL} /* Sentinel */ };

static PyObject * Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored)) { return PyUnicode_FromFormat(“%S %S”, self->first, self->last); }

static PyMethodDef Custom_methods[] = { {“name”, (PyCFunction) Custom_name, METH_NOARGS, “Return the name, combining the first and last name” }, {NULL} /* Sentinel */ };

static PyTypeObject CustomType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = “custom3.Custom”, .tp_doc = “Custom objects”, .tp_basicsize = sizeof(CustomObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_new = Custom_new, .tp_init = (initproc) Custom_init, .tp_dealloc = (destructor) Custom_dealloc, .tp_members = Custom_members, .tp_methods = Custom_methods, .tp_getset = Custom_getsetters, };

static PyModuleDef custommodule = { PyModuleDef_HEAD_INIT, .m_name = “custom3”, .m_doc = “Example module that creates an extension type.”, .m_size = -1, };

PyMODINIT_FUNC PyInit_custom3(void) { PyObject *m; if (PyType_Ready(&CustomType) < 0) return NULL;

m = PyModule_Create(&custommodule);
if (m == NULL)
    return NULL;

Py_INCREF(&CustomType);
PyModule_AddObject(m, "Custom", (PyObject *) &CustomType);
return m; }