升級 daphne 到 1.2.0 後導致連線卡住

使用 django channels 得使用 daphne 來處理 asgi (websocket/long poll http) 請求,我遇到的情況是從 1.1.0 升級到 1.2.0 以後,整個 django 應用就無法接受服務。

查了很久,看程式也覺得應該沒問題,該處理的 request ,daphne 都有轉送給 channels 去處理,而 channels 也都有處理。心裡想,這應該是個大問題,早該有人回報吧。但用 google 查詢都沒查到,今天心血來潮查了 Daphne 在 github 上的 closed issues,總算是找到了:daphne==1.2.0 hangs forever · Issue #105 · django/daphne

解法也很簡單,升級 asgi_redis/asgi_ipc 到新版就可以了。下次升級 daphne 時,得注意。

anbox 與 snappy

Anbox 是一個以 container 來摹擬 Android 的技術,如此一來,就可以在 Ubuntu 裡執行 Android app。

按照 Anbox 的說明,得先安裝 snappy,snappy 是 Canonical 推的新的套件機制,軟體以類似 container 的方式安裝進去,不過這個技術推行快兩年了,用的人好像還是不怎麼多。安裝 snappy 的方法很簡單,只要安裝 snapd 就可以了:

sudo apt-get install snapd

安裝 snappy 以後,你還得要先登入 Ubuntu one 的帳號 (用法):

sudo snap login your_email

,之後才能依照 Anbox 的說明,進行安裝:

snap install --classic anbox-installer

。如果不先 login ,snap install 時,會不給安裝。

安裝 anbox-installer 以後,再執行

anbox-installer

就會啟動安裝程序了。

不過我還是安裝失敗了,anbox 說 Ubuntu 14.04 應該是可以安裝,但是實際狀況是有問題,我沒深究,就算了。

docker daemon socket

dockerd 預設只使用 /var/run/docker.sock,如果需要讓其他的 host 去管理,就需要額外設定。

在 Ubuntu trusty 裡,要編輯 /etc/default/docker,加入

DOCKER_OPTS="-H 0.0.0.0:2375"

然後重新啟動就可以了。

不過這樣設定以後,就等於任何人都可以透過這個 port 去控制你這台機器裡的所有 container ,官方建議加上 TLS 來保障連線的安全,晚點再來研究怎麼加上 TLS 。

參考資料:

pipdeptree

前幾天上傳程式到 openshift 時,發現新版的 six 安裝不上去,原因是 OpenShift 所帶的 six 是 0.3 版,但是無法透過 sudo 等之類的方式來安裝新版,所以想查到底是哪個模組會需要用到新版的 six。上網找了一下,發現 pipdeptree 可以滿足我的需求。

用 pip install pipdeptree 就可以安裝,使用上也很簡單,輸入 pipdeptree 就會列出各個模組的相依性與其所需最低版本。

結果大致是這樣:

Warning!!! Possibly conflicting dependencies found:
* HaodooScraper-Flask==1.0
 - python-dateutil [required: ==2.3, installed: 2.6.0]
 - six [required: ==1.3.0, installed: 1.10.0]
------------------------------------------------------------------------
alembic==0.6.7
  - Mako [required: Any, installed: 1.0.0]
    - MarkupSafe [required: >=0.9.2, installed: 0.23]
  - SQLAlchemy [required: >=0.7.3, installed: 0.9.8]
cssselect==0.9.1
Flask-APIBlueprint==1.0.0
  - flask [required: >=0.11.1,<1.0, installed: 0.12] - click [required: >=2.0, installed: 6.7]
    - itsdangerous [required: >=0.21, installed: 0.24]
    - Jinja2 [required: >=2.4, installed: 2.9.5]
      - MarkupSafe [required: >=0.23, installed: 0.23]
    - Werkzeug [required: >=0.7, installed: 0.12.1]
  - six [required: <2.0,>=1.10.0, installed: 1.10.0]
HaodooScraper-Flask==1.0
  - Flask [required: Any, installed: 0.12]
    - click [required: >=2.0, installed: 6.7]
    - itsdangerous [required: >=0.21, installed: 0.24]
    - Jinja2 [required: >=2.4, installed: 2.9.5]
      - MarkupSafe [required: >=0.23, installed: 0.23]
    - Werkzeug [required: >=0.7, installed: 0.12.1]
  - Flask-Bootstrap [required: Any, installed: 3.3.7.1]
    - dominate [required: Any, installed: 2.3.1]
    - Flask [required: >=0.8, installed: 0.12]
      - click [required: >=2.0, installed: 6.7]
      - itsdangerous [required: >=0.21, installed: 0.24]
      - Jinja2 [required: >=2.4, installed: 2.9.5]
        - MarkupSafe [required: >=0.23, installed: 0.23]
      - Werkzeug [required: >=0.7, installed: 0.12.1]
    - visitor [required: Any, installed: 0.1.3]
  - Flask-JsonTools [required: Any, installed: 0.1.1.post0]
    - flask [required: >=0.10.1, installed: 0.12]
      - click [required: >=2.0, installed: 6.7]
      - itsdangerous [required: >=0.21, installed: 0.24]
      - Jinja2 [required: >=2.4, installed: 2.9.5]
        - MarkupSafe [required: >=0.23, installed: 0.23]
      - Werkzeug [required: >=0.7, installed: 0.12.1]
  - flask-restplus [required: Any, installed: 0.10.1]
    - aniso8601 [required: >=0.82, installed: 1.2.0]
      - python-dateutil [required: Any, installed: 2.6.0]
        - six [required: >=1.5, installed: 1.10.0]
    - Flask [required: >=0.8, installed: 0.12]
      - click [required: >=2.0, installed: 6.7]
      - itsdangerous [required: >=0.21, installed: 0.24]
      - Jinja2 [required: >=2.4, installed: 2.9.5]
        - MarkupSafe [required: >=0.23, installed: 0.23]
      - Werkzeug [required: >=0.7, installed: 0.12.1]
    - jsonschema [required: Any, installed: 2.6.0]
    - pytz [required: Any, installed: 2017.2]
    - six [required: >=1.3.0, installed: 1.10.0]
  - Flask-WTF [required: Any, installed: 0.14.2]
    - Flask [required: Any, installed: 0.12]
      - click [required: >=2.0, installed: 6.7]
      - itsdangerous [required: >=0.21, installed: 0.24]
      - Jinja2 [required: >=2.4, installed: 2.9.5]
        - MarkupSafe [required: >=0.23, installed: 0.23]
      - Werkzeug [required: >=0.7, installed: 0.12.1]
    - WTForms [required: Any, installed: 2.1]
  - lxml [required: Any, installed: 3.4.0]
  - python-dateutil [required: ==2.3, installed: 2.6.0]
    - six [required: >=1.5, installed: 1.10.0]
  - requests [required: Any, installed: 2.13.0]
  - six [required: ==1.3.0, installed: 1.10.0]
  - SQLAlchemy [required: Any, installed: 0.9.8]
nose==1.2.1
pipdeptree==0.10.1
  - pip [required: >=6.0.0, installed: 9.0.1]
pkg-resources==0.0.0
PyMySQL==0.6.2
pyOpenSSL==0.14
  - cryptography [required: >=0.2.1, installed: 1.8.1]
    - asn1crypto [required: >=0.21.0, installed: 0.22.0]
    - cffi [required: >=1.4.1, installed: 1.10.0]
      - pycparser [required: Any, installed: 2.17]
    - idna [required: >=2.1, installed: 2.5]
    - packaging [required: Any, installed: 16.8]
      - pyparsing [required: Any, installed: 2.2.0]
      - six [required: Any, installed: 1.10.0]
    - setuptools [required: >=11.3, installed: 34.3.3]
      - appdirs [required: >=1.4.0, installed: 1.4.3]
      - packaging [required: >=16.8, installed: 16.8]
        - pyparsing [required: Any, installed: 2.2.0]
        - six [required: Any, installed: 1.10.0]
      - six [required: >=1.6.0, installed: 1.10.0]
    - six [required: >=1.4.1, installed: 1.10.0]
  - six [required: >=1.5.2, installed: 1.10.0]
wheel==0.30.0a0

Django queryset 對日期時間欄位的額外設定

Django queryset 對日期時間的處理已經很完備了,可以透過使用 __year 或 __month 等方式來找到是某年或某月的紀錄。

這兩天碰到的狀況是,資料是 MySQL 時,日期時間的比對 (__year / __month) 失效了。仔細看過文件以後,才發現 MySQL 需要事先設定,使用 mysql_tzinfo_to_sql 載入時區表格才行。

This function performs time zone conversions directly in the database. As a consequence, your database must be able to interpret the value of tzinfo.tzname(None). This translates into the following requirements:

在終端機 (shell) 裡,輸入下列指令:

mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql

接著重新啟動 MySQL 伺服器即可。

mysql_tzinfo_to_sql 的用法不只一種,我選擇的是最簡單的用法。

Amazon Cloud drive on Ubuntu

安裝 acd_cli 就可以了。

安裝跟使用可以參閱 https://acd-cli.readthedocs.io/en/latest/ ,用 pip3 install
git+https://github.com/yadayada/acd_cli.git 就安裝完了。不想裝到系統 /usr/local 的話,加上 –user,可以裝到 $HOME/.local 裡。

第一次使用要先 acd_cli sync,之後就可以使用上傳等功能了,最方便的是,acd_cli 可以利用 FUSE 掛載在某個資料夾下,這樣就可以用檔案總管或是 rsync 一類的軟體來處理了。

參考資料:

docker-compose.yml 裡的相依性與啟動順序

簡單的說,depends_on 跟 links 只保證相依的 container 有啟動,但不保證裏面的服務已經啟動完畢,可以接受服務了。

我找到的解決方法,是利用 wait-for-it 這個 bash script 來測試 container 的 port 是否開啟。docker-compose v3 好像有要試圖解決這問題,不過還沒正式釋出。也是有一些其他的解法,不過,我覺得不是很直覺,就先放著了。

參考資料:

 

電影流水帳(2017/3/2~2017/3/19)

白歆惠-裸白歆惠,知名的模特兒,後來轉戲劇,演了不少連續劇跟電影,是銷售奇姬的女主角。

  • 一萬公里的約定 (IMDB, Wikipedia)。這片真的…不好看。劇情太破碎,是可以猜到為什麼,但是這樣說故事實在是讓人看不下去。結局也交代的不好,看起來育幼院是募到資金了,但是以晴去密醫那兒開心以後怎麼了?哥哥呢?為什麼要送以晴去作換心手術,那自己的妻兒是不用管了喔?純粹的灑狗血。
    故事是描述弟弟立志參加超級馬拉松的過程。兄弟倆的父親是跳遠選手,但是後來卻只能落魄的過日子。哥哥逃家去外地唸書、練馬拉松,弟弟後來也跟著去了,對,他們就這樣把老父丟在家裡。弟弟在哥哥那邊遇到了助理教練以晴,接受她的訓練,而且日久生情。哥哥則是讓女友未婚懷孕,為了要賺錢養孩子,跑去運毒。後來,弟弟因為以晴走了,就停止練習跑超馬,哥哥則繼續運毒。弟弟為了生活,跑去開計程車,遇到周杰倫,這段還蠻勵志的。然後跑去阻止哥哥,之後又莫名其妙的幫起哥哥運毒。總之,過了一陣子,弟弟某天遇到以晴,知道以晴的病情,決定重新開始認真跑超馬,並且變得有名起來。最後,則是哥哥為了讓以晴,推去找密醫換心,弟弟則是成功募到育幼院款項,完成以晴心願,大概就這樣。
  • 超級快遞 (IMDB, Wikipedia)。中韓合作的電影,女主角是 Running Man 的宋智孝,宋智孝在裏面飾演一個保安主任,實則是一個偷兒,電影的節奏緊湊,還不錯。
    陳赫飾演快遞員馬力,原來是個賽車手,後來因故改當快遞員。他有個女友娜娜,已經論及婚嫁。某天他送貨到王三表家時,遇到美熙,他感到奇怪,明明應該是王三表出來的,卻是美熙。不過還是讓她簽收了,就在要離開時,遇到 Gary 來追討快遞的物品,因而捲入這樁怪事。
    後來透過美熙才知道,原來美熙是保安主任,Gary 從博物館裡偷走,美熙則是追著這個古物來到這裡。經過幾番與 Gary 的爭奪,馬力幫美熙取回古物,可是卻意外翻盤,美熙居然也是一個偷兒。Gary 後來挾持了馬力的女友,要馬力拿古物來換,可是美熙已經到了機場,準備要走,馬力發了訊息,決定單槍匹馬去救人。最後,美熙仍是心軟,回來幫了馬力,救回馬力的女友,古物也歸還,皆大歡喜。
  • 銷售奇姬 (IMDB, Wikipedia)。最近衛視電影台開始放送的電影,跟電視購物有關係的故事。
    陳夙芬‬因為失業變得很失意,好不容易找到一份在賣場銷售的工作,可是卻因為拉不下臉而再次面臨失業的危險。在潔哥要找蘿絲復仇的陰錯陽差之下,夙芬變成潔哥的弟子,銷售技巧在潔哥指導之下,脫胎換骨,而且因為原本首席的美秀離職,成為賣場的首席。
    夙芬騎驢找馬,去電視購物公司面試,卻巧遇美秀。兩人在電視購物公司總經理的賞識下,進去上班。美秀對於夙芬始終抱有敵意,一直覺得夙芬並不尊重銷售的工作。夙芬則是誤打誤撞,慢慢闖出一番局面。不過職場順利,就忽略了家裡。夙芬的老公智凱為了讓夙芬離開購物公司,找了之前意外遇到的蘿絲幫忙,讓她去破壞夙芬的事業,卻意外讓夙芬愈來愈好。
    嗯,從這邊到結局,我沒看到,因為被迫要離開。結局大致上就是夙芬離開購物公司,回到家裡,而潔哥與蘿絲重歸舊好,美秀也拋開了對夙芬的敵意,皆大歡喜。
  • Tropic Thunder (IMDB, Wikipedia),台譯:開麥拉驚魂。這部電影以前看過一次了,再重看。
    前面的片頭非常有趣,大致上是介紹電影裡主角們之前的豐功偉業,但是他們都過了巔峰的時候,開始走下坡,大家想靠這部電影重新走紅。導演是個名不見經傳的新導演,沒辦法駕馭這幾個演員,於是聽信劇本原著的意見,將這些人帶到真實的叢林裡去,進行真槍實彈的拍片。
    一行人在叢林裡遇到真正的毒梟,導演誤踩地雷先掛了,其他演員則認為是導演故意的,就照著劇本在叢林裡穿梭、拍戲。到了後來,演員們發現了自己真的被困在叢林裡,並且必須面對兇狠的毒梟。特效組則跟劇本原著去叢林救人,卻意外發現劇本原著是騙子,劇本故事也是假的,兩人後來被毒梟抓走。
    Tugg 也被毒梟捉走,但因為毒梟們很喜歡 Simple jack 這部電影,而放過 Tugg,只要求他持續演出 Jack 這個角色。一行人為了救出 Tugg ,就計劃了行動,進入毒梟村落救人。最終所有人都逃脫出來,原本 Tugg 還在猶豫要不要留在毒梟村落裡扮演 Simple jack ,但村民遠超乎 Tugg 想像之外,Tugg 當下就決定回去了。
    電影裡很多大牌以出人意料的方式演出,像是 Robert Downey Jr., Tom Cruise, Matthew McConaughey 等,片尾 Tom Cruise 的熱舞真的是經典了。
  • 行運超人 (IMDB, Wikipedia)。很久以前看的電影,發現自己沒紀錄。故事一開始先帶出為什麼葉孤紅那麼倒楣的原因,然後帶出葉孤紅去找賴料布改變自身命運並找到如意郎君的經過。蠻輕鬆有趣的故事,可以打發掉不少時間。今年看歌手2017時,意外發現原來演葉太的杜麗莎居然是個唱歌的老前輩,教過不少歌手唱歌,也在發現原來楊千嬅跟鄭中基有過一段情。

 

docker-machine 的 port forwarding

最近在 Windows 試 docker-toolbox,想讓外部連線連到 host 時,其實是連接到 在 VM 裡服務的 docker container,所以查了看怎麼做。

結果比想像中還要簡單,就是利用 vboxmanage controlvm 去設定 port forwarding 就可以了。

/c/Program\ Files/Oracle/VirtualBox/VBoxManage.exe controlvm vm_name natpf1 "rule1,tcp,,80,,80"

那要刪除掉這條 port forwarding 的規則,用

/c/Program\ Files/Oracle/VirtualBox/VBoxManage.exe controlvm vm_name natpf1 delete rule1

即可。

那在 linux 下也可以用同樣的方式來做 port forwarding,唯一的限制是非 root 用戶,沒辦法做 port 1024 以下的 port forwarding。

說明:

  • docker-toolbox 其實是把 docker, docker-machine, docker-compose, virtualbox 等工具包裝起來的一個便捷安裝包
  • vm_name 可以用 vboxmanage list vms 取得
  • 也可以開啟 VirtualBox ,在左側選到虛擬機,再進設定去改動。

參考資料:

mypy

演講:https://youtu.be/ZP_QV4ccFHQ

mypy 是靜態型別檢查工具,遵循 PEP484 與 PEP526 這兩個標準

而 PEP484 與 PEP526 這兩個標準最主要的用途是為 Python 加上型別標示,這樣可以更容易找到潛在的錯誤。

安裝

pip3 install mypy-lang

程式的部份 (type annotation)

Python 3

def hello(name: str) -> str:
    """foo."""
    return "Hello, {}".format(name)


def main() -> None:
    """Main entry."""
    hello("John")

Python 2

def hello(name):
    # type: (str) -> str
    """foo."""
    return "Hello, {}".format(name)


def main():
    # type: () -> None
    """Main entry."""
    hello("John")

如何使用 mypy 檢查

如果程式沒加 type annotation,那麼執行 mypy 不會有任何問題。 如果有一部份使用了 type annotation,那麼執行 mypy 就會告知有問題。 要強制檢查,可以使用 –disallow-untyped-defs

mypy --disallow-untyped-defs your_program.py

要產生 HTML 報告

mkdir html
mypy --html-report ./html your_program.py
xdg-open ./html/index.html

產生 JUnit XML

mypy --junit-xml JUNIT_XML your_program.py

案例

Dropbox 內部已經在使用。