uv:用 Rust 寫的 Python Packaging 替代方案

社群好幾個地方都有提到的「uv: Python packaging in Rust」這個,文章開頭的說明有快速說明目標是 pip 的 drop-in replacement:

TL;DR: uv is an extremely fast Python package installer and resolver, written in Rust, and designed as a drop-in replacement for pip and pip-tools workflows.

這跟「Ruff:用 Rust 寫的 Python Linter」都是 Astral 下的專案,主打用 Rust 改善速度的專案。

馬上想到的是 package resolver,這指的是依照每個套件指定的相依條件,找出符合所有條件的版本組合。

這在各個語言的套件系統上都是痛點,而在「Dependency hell is NP-complete」這篇就有指出這是 NP-complete

這是因為 3SAT 問題可以 PTIME 轉成 package resolver 問題 (於是就 NP-hard 了),再加上有 PTIME 的驗證,就變成 NP-complete 了。

但看說明應該是不只這個部分,包括了一些 i/o 類操作的改善。

除了速度以外,uv 也提供了讓測試更方便的功能,像是在計算相容版本時,預設的演算法是儘量都裝最新版,但你可以指定要儘量裝最舊的版本,這樣對於相容性測試頗有用的:

But by passing --resolution=lowest, library authors can test their packages against the lowest-compatible version of their dependencies. (This is similar to Go's Minimal version selection.)

這個工具的出現也是頗有幫助,我記得寫 Python 專案時隨便引入個 Django,再多拉幾個套件,跑起 package resolver 就要花不少時間了,可以想像中大型專案在這塊的痛點...

另外剛剛回去看了 ruff,從去年四月 500+ 條規則增加到 700+ 條了,在發表受到注目後應該補了不少社群常用到的規則,說不定新專案可以無痛跳進去了,去年的時候試著用,有發現常見的規則還沒有支援...

OpenLLM,用 Python 包裝 open source LLM 的套件

Hacker News 上看到「OpenLLM (github.com/bentoml)」,是一個用 Python 寫的軟體,把 open source LLM 包裝起來讓你用。

先拿 Mac 簡單測了一下,看起來包的不錯,可以用 HTTP API 來打。

先用 pip 裝:

pip install openllm

然後就可以把 server 跑起來了,依照範例跑 dolly-v2,第一次跑會比較久,需要下載 model:

openllm start dolly-v2

接下來就可以直接開 http://127.0.0.1:3000/ 來操作了,另外也可以用 command line 跑,像是依照官方的範例來跑:

openllm query --endpoint http://127.0.0.1:3000 "What is the meaning of life?"

目前測到比較明顯的問題是 CPU 模式下只有 single thread,所以雖然會動,但相當慢... 之後再來測試 GPU 的部分。

Python 上的 reals 套件 (需要 3.10+ 以上才能裝)

看到「A lightweight python3 library for arithmetic with real numbers.」這個有趣的 Python 延伸套件,可以用他進行高精度的實數運算...

一開始在 Python 3.9 環境裝,結果就跳出需要 3.10+ 的環境,想了一下,開了一個 Docker container 裝 pyenv 來測,測過以後覺得還蠻有趣的,看起來之後把預設環境變成 3.10+ 應該會裝起來用...

這個 reals 的重點在於保證顯示數字的正確性:

It allows you to compute approximations to an arbitrary degree of precision, and, contrary to most other libraries, guarantees that all digits it displays are correct.

目前支援的常數與操作有這些:

Constants: pi, e, phi
Functions related to powers: sqrt, exp, log
Operators: negation, addition, subtraction, multiplication, division, powers
Trigonometric functions: sin, sinh, csc, csch, cos, cosh, sec, sech, tan, tanh, cot, coth

用法的部份,先把 reals 拉進來:

>>> from reals import sqrt

然後用法算直覺:

>>> sqrt2 = sqrt(2)
>>> sqrt2
<reals._real.Real object at 0x10d182560 (approximate value: 1.41421)>
>>> sqrt2.evaluate(10)
'1.4142135624'
>>> '{:.10f}'.format(sqrt2)
'1.4142135624'
>>> sqrt2.to_decimal(10)
Decimal('1.4142135624')

不過作者有提到效能沒有處理到很好,所以應該是拿來快速做一些運算得到結果而已。

Pyston 改變方向,將主推模組載入的方式使用

Pyston 專案是一個想要提供更快速的 Python,而前陣子決定改變開發的方向:「Announcing 3.7-3.10 support and a new direction」。

本來的 Pyston-full 是直接修改 CPython 的 codebase 加速:

Our original product, which we’re retroactively calling Pyston-full, is a fork of the entire CPython codebase. Having users install a fully-custom version of Python lets us make changes across the Python implementation, leading to the most optimizations and largest speedups.

但這種方式的安裝與維護都需要另外搞,而且因為 ABI 不相容的問題,遇到一些套件可能會需要自己編 (甚至自己改?),不能直接用編好的 binary:

The flip side is that it is fairly intensive to set up. While we believe Pyston-full is one of the most highly-compatible alternative Python implementations available, it can be difficult to switch Python implementations regardless of the ease of use of either implementation. Compounded on this, we decided to break the ABI which requires users to recompile extension modules. In theory this is not a big deal, but in practice the lack of available binary packages is a significant disincentive to use an alternative implementation.

這樣雖然有 30% 的效能提昇,但對使用者的吸引力不高,所以打算要轉變方向,讓使用者更容易使用,這也是決定發展可以用 pip 安裝的 Pyston-lite 版本:

The sum of all of this was that while we were very happy to achieve a 30% speedup with Pyston-full, it was very difficult to get people to start using it. We decided to try a different form factor: a pip-installable extension module called Pyston-lite.

但效能的提昇就不像 Pyston-full 這麼高,Pyston-lite 只剩下 10% 了:

So while it’s a bit difficult to accept that we are now providing a 10% speedup instead of 30%, we’ve decided that it’s much more important to provide something that people are willing to use.

另外在文末有列出各版本的效能提昇 (與 CPython 3.8 比較),可以看到 CPython 3.11rc2 的提昇其實跟 Pyston-lite 差不多,除非 Pyston-lite 可以把效能疊加上去,不然就有點尷尬了:

但 Pyston 要支援 3.11 看起來會花不少功夫:

In the longer-term future we are planning to submit our JIT upstream as well, but we expect retargeting it to 3.11 to be significantly more work than the other versions due to the extensive amount of changes that were made to the interpreter in that version.

不過手上一些既有的東西好像可以掛上去測看看...

在本機用 pip 直接安裝 PostgreSQL server

看到 PostgreSQL 官方站台上的介紹,可以直接用 Pythonpip 指令安裝 PostgreSQL server:「Install a local, non-root PostgreSQL Server with Python "pip"」,專案在「postgresql-wheel」這邊。

GitHub 上面的說明跑了一下,還真的可以惡搞... 這樣如果真的要在 CI 裡面跑的話也簡單很多了?只要能 pip 裝軟體就能跟你拼 XDDD

也省掉需要設定一些權限跑 Docker-in-Docker...

Facebook 放出 Pysa,靜態分析 Python 程式碼的工具

Facebook 丟出來的靜態分析工具,可以拿來分析 Python 程式碼:「Pysa: An open source static analysis tool to detect and prevent security issues in Python code」,專案在「facebook/pyre-check」這邊可以取得。

不過軟體居然是用 OCaml 寫的啊,另外已經包好了,可以用 pip 直接裝 pyre-check

官方的說明裡面有提到要裝 watchman,不過這算是選擇性安裝,不裝 watchman 直接執行也可以用,只是會跳個訊息跟你說裝了可以遞增檢查:

To enable pyre incremental, you can install watchman: https://facebook.github.io/watchman/docs/install

最近在寫的專案都是用 Python,剛好可以拿來掃這些專案...

Nginx + FastCGI + Trac

先前試著逼自己用 Phabricator,用了一個多月後發現設計的邏輯還是跟 Trac 差了不少,算是為了 Facebook 特化的產品吧。在這一個月查資料的過程也發現當初 Wikimedia 要採用的時候也花了不少力氣送 patch 回官方,然後針對不少地方客製化調整。

另外比較痛的地方是 plugin 的支援能力還沒有很好,變成很多東西都要改主體... 而且效能也不太好 (不支援 PHP 7.0 還蠻痛的),在比較低階的 VPS 上跑特別明顯。

這幾天花了點時間把 Trac 給架起來,之前都是用 FreeBSD ports 架,但已經愈來愈沒有再接觸 FreeBSD 了,所以這次在 Ubuntu 上用 pyenv 裝起來再用 pip 裝起來。

另外一個跟之前不同的,是先前都用 Apachemod_wsgi,在低階的 VPS 上則是要找省資源的方案,這次則是用 nginx + FastCGI 去接,比起之前複雜不少...

最主要是參考了官方的文件以及「Gentoo下使用nginx+fastcgi部署trac」這邊的說明達到效果,重點是這段 location 的設定:

    location / {
        auth_basic "trac realm";
        auth_basic_user_file /srv/domain.example.com/.htpasswd;

        include fastcgi.conf;
        fastcgi_param AUTH_USER $remote_user;
        fastcgi_param HTTPS on;
        fastcgi_param PATH_INFO $fastcgi_script_name;
        fastcgi_param REMOTE_USER $remote_user;
        fastcgi_param SCRIPT_NAME "";
        fastcgi_pass unix:/var/run/trac/trac.sock;
    }

    location /.well-known/acme-challenge/ {
        alias /var/www/dehydrated/;
    }

網路上有找到用 location ~ (/.*) 去 match,然後拉出 $1PATH_INFO 用的,這這會使得這段 location 的優先權太高 (參考官方對於 location 的順序說明),而蓋掉下面 Let's Encrypt 的 acme challenge 過程,所以還是得這樣搞。

另外是自己一個人用,就用 .htpasswd 的方式認證了,沒必要弄 LDAP 之類的認證...

接下來就是裝一堆 plugin 並且調整 css/js 與 SQL query 了...

在 Python 的 pip、Nodejs 的 npm、Ruby 的 RubyGems 上面放木馬研究?

在 Python 領域裡常用 pip 安裝軟體:

$ pip install reqeusts

或是:

$ sudo pip install reqeusts

其他的平台也大致類似於這樣的動作。而在「Typosquatting programming language package managers」這篇文章裡,作者用 typo 之類的方式列出可能的名稱,像是這樣的名稱:

$ sudo pip install reqeusts

然後在這三個平台上發動攻擊,上傳了數百個套件並且觀察:

All in all, I created over 200 such packages and equipped them with a small program and uploaded them over the course of several months. The idea is to add some code to the packages that is executed whenever the package is downloaded with the installing user rights.

而這是「成果」:

AWS 推出的 aws-shell

AWS 推出的 aws-shell 透過大量的 auto completion 幫助你管理 AWS 的各種資源,可以從官方說明看出是 AWS Command Line Interface (aws-cli) 的延伸:

An integrated shell for working with the AWS CLI.

程式是用 Python 寫的,可以透過 pip 直接安裝到自己的目錄下,官方說明支援 2.6.5+、2.7+、3.3+、3.4+,所以應該是不會有太多問題:

$ pip install aws-shell

第一次執行的時候放著讓他背景建立 index 會比較順。

e95d85da-a328-11e5-8b8d-67566eccf9e3

3648b4fc-a32c-11e5-8e18-92f028eb1cee

可以看到功能相當強,甚至可以連到 AWS 上取得資料。

建議 GitHub 上的首頁說明部份都先看完,然後裝起來先測試玩看看,對於真的要用的時候會方便很多。

另外他執行時真的丟到 shell 去跑,所以你可以 pipe 出去處理,像是這樣:

ec2 describe-instances | jq .