Ubuntu trusty python2 與 python3 的 PYTHONPATH

python2 的 sys.path 是

['',
 '/usr/local/lib/python2.7/dist-packages/python_nghttp2-1.3.5.dev0-py2.7-linux-x86_64.egg',
 '/usr/lib/python2.7',
 '/usr/lib/python2.7/plat-x86_64-linux-gnu',
 '/usr/lib/python2.7/lib-tk',
 '/usr/lib/python2.7/lib-old',
 '/usr/lib/python2.7/lib-dynload',
 '/home/ellery/.local/lib/python2.7/site-packages',
 '/usr/local/lib/python2.7/dist-packages',
 '/usr/lib/python2.7/dist-packages',
 '/usr/lib/python2.7/dist-packages/PILcompat',
 '/usr/lib/python2.7/dist-packages/gtk-2.0',
 '/usr/lib/pymodules/python2.7',
 '/usr/lib/python2.7/dist-packages/ubuntu-sso-client',
 '/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode']

 

python3 的 sys.path 是

['',
 '/usr/local/lib/python3.4/dist-packages/onedrive_d-1.1.0dev-py3.4.egg',
 '/usr/local/lib/python3.4/dist-packages/daemonocle-0.8-py3.4.egg',
 '/usr/local/lib/python3.4/dist-packages/Send2Trash-1.3.0-py3.4.egg',
 '/usr/local/lib/python3.4/dist-packages/certifi-2015.11.20.1-py3.4.egg',
 '/usr/local/lib/python3.4/dist-packages/psutil-2.1.1-py3.4-linux-x86_64.egg',
 '/usr/lib/python3/dist-packages',
 '/usr/lib/python3.4',
 '/usr/lib/python3.4/plat-x86_64-linux-gnu',
 '/usr/lib/python3.4/lib-dynload',
 '/home/ellery/.local/lib/python3.4/site-packages',
 '/usr/local/lib/python3.4/dist-packages']

 

會發現兩邊的 $HOME/.local/lib/pythonX.Y/site-packages 順序是不一樣的,照理來說,應該是要以 $HOME/.local/lib/pythonX.Y/site-packages 為優先,這樣才有機會安裝最新版本的 package 而無需更動到系統的 package。

那該怎麼辦呢?這可以修改 /etc/python3.4/sitecustomize.py 來解決這問題。

在 /etc/python3.4/sitecustomize.py 的最後加入

import os
import sys

sys.path.insert(0,
        os.path.join(
            os.path.expanduser("~"),
            ".local",
            "lib",
            "python3.4",
            "site-packages"))

Python3 在執行前,會先執行這個檔案,這裡在 sys.path 串列最前面插入 $HOME/.local/lib/pythonX.Y/site-packages ,就能解決這問題了。

參考資料:

Python on Visual Studio Code 初步設定

Visual Studio Code 有獨立一個頁面介紹 Python 的部份:Python with Visual Studio Code

基本有4個 extension 要裝:

  • Python
  • Code Runner
  • MagicPython
  • Python for VSCode

Python 這個 extension 可以設定的東西很多,設定可以參考它的 Wiki ,可是知道有這些設定以後,該怎麼設定呢?你得先在專案資料夾下建立一個 .vscode 的資料夾,然後在裏面新增一個 settings.json ,這邊再參考 Wiki 去填入設定就可以了。

目前我的 Django 專案用的設定是這樣:

{
    "python.pythonPath": "/home/user/.virtualenvs/myproject/bin/python",
    "python.linting.flake8Enabled": true,
    "python.formatting.provider": "autopep8",
    "name": "Django",
    "type": "python",
    "request": "launch",
    "stopOnEntry": false,
    "program": "${workspaceRoot}/manage.py",
    "args": [
        "runserver",
        "--noreload"
    ],
    "debugOptions": [
        "WaitOnAbnormalExit",
        "WaitOnNormalExit",
        "RedirectOutput",
        "DjangoDebugging"
    ]
}

 

因為我用的是 virtualenv ,所以 python 執行檔跟相關的 library 都是在 $HOME/.virtualenv 這裡,把 python.pythonPath 設到這裡以後, intellisense/autocomplete 就可以正常運作。此外我也加上了 linting 與執行、除錯的相關設定。

其他還有 Jupyter 等等的,還沒去深入了解,再找時間來看。

電影流水帳(2017/3/20~2017/5/9)

Emma Watson_85 Emma Watson ,最著名的角色就是妙麗了,這次看到她,是在 This is the end 裡飾演 Emma Watson 去參加 Franco 的派對,蠻令人意外的演出。

  • 眠りの森 (IMDB, Wikipedia),台譯:沈睡的森林。由東野圭吾小說改編的故事,跟騏驎之翼,是加賀恭一郎的辦案故事。衝著阿部寬,我把它看完了,相當好看,讓我想找小說來看看了。
    芭蕾舞團發生了疑似過失殺人的意外,加賀恭一郎跟當地的刑警介入調查。在調查的過程裡,兩人發現並不是過失殺人這麼簡單,於是他們積極的與芭蕾舞團裡的人聊天,進行調查,甚至也在經費可能不足的情況下,強力要求去美國紐約查案。
    最後在恭一郎送東西去找未緒,和未緒聊完天,在大雨中要回家的時候,突然靈機一動,把前面的所有線索都連結起來,知道了真兇是誰。於是,他們讓芭蕾舞團演完這齣睡美人之後,逮捕了真正的凶手 – 未緒。未緒在亜希子與風間發生爭吵的時候,意外殺了風間,但葉瑠子、亜希子為了讓即將失聰的未緒能演出最後一齣戲,聯手隱藏了真相,偽裝是葉瑠子意外殺了風間。故事的結局不但出人意表,未緒的遭遇也令人同情。
  • Due date (IMDB, Wikipedia),台譯:臨門湊一腳。不能算好看又好笑的電影,但還蠻適合用來打發時間的。
    Peter 的妻子要生了,他趕著要回洛杉磯,在飛機上遇到要去好萊塢面試的 Ethan,兩人不對盤,對話的時候被誤會有帶炸彈,就被請下飛機,兩人從此不能搭飛機。Peter 的錢包掉了,駕照跟錢都在裏面,沒辦法租車,Ethan 伸出援手,兩人一同開車去洛杉磯。在往洛杉磯的路上,兩人從一開始的不對盤,到慢慢熟識、相互了解,最後總算是攜手回到洛杉磯,順利到醫院,大概就這樣。不過 Peter 還是沒看到孩子的出生,因為兩人從墨西哥逃回美國的路上,Peter 被槍打中,到醫院時,正好去急救,Ethan 就代替 Peter 接生了孩子。
  • This Is the End (IMDB, Wikipedia),台譯:大明星世界末日。裡面的角色都飾演自己,故事很簡單,Jay 去找 Seth,本想兩個好朋友度過一個快樂的周末,可是 Seth 拉了 Jay 去參加 Franco 的派對。Jay 在派對裡感到落寞,拉了 Seth 去外面買東西,突然天崩地裂,一堆人被藍光吸走,兩人趕緊逃回 Franco 的家。Jay 跟 Seth 正在述說外面的情況時,Franco 的家也發生了同樣的事情,一堆人奪門而出逃走,有些人掉到深淵,有些人發生了意外。Franco、Jay、Seth、Jonah、Craig 一行人逃回 Franco 的家裡,於是開始了末日求生。接著就是述說他們在 Franco 家裡的一些事情,有些很白爛,有些很有趣。電影的最後是 Jay 發現了被藍光吸走其實是被帶領到天堂,所以得做些好事才能得救。Seth、Jay 跟 Craig 幸運的以這個手法得到救贖,並上到天堂。
  • Guardians of the Galaxy Vol. 2 (IMDB, Wikipedia),台譯:星際異攻隊2。有維持前作的水準,除了故事好以外,配樂也相當的不錯,在幾個重要的場面所播放的歌曲都很搭。
    這次主要是 Peter 的尋親之旅,異攻隊一行人去幫天神族,但手賤的 Rocket 偷了天神族的電池,在逃走途中,意外被 Peter 的父親 Ego 所救。Rocket, Groot 跟 Nebula 留下修理太空船,其他人跟著 Ego 回去 Ego 的星球。經過一番確認,Peter 確信自己找到父親,非常的高興。可是父親卻別有居心,異攻隊一行人發現不對勁,跟著後來追來的 Yondu 聯手對抗 Ego。經過一番苦戰,粉碎 Ego 的野心,再次拯救世界。最後的結局有淡淡的哀傷,人真的要把握當下啊!

升級 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 好像有要試圖解決這問題,不過還沒正式釋出。也是有一些其他的解法,不過,我覺得不是很直覺,就先放著了。

參考資料: