2021年5月29日 星期六

[Linux 常見問題] Find and kill a process in one line using bash and regex

 Source From Here

I often need to kill a process during programming.

The way I do it now is:
[~]$ ps aux | grep 'python csp_build.py'
user 5124 1.0 0.3 214588 13852 pts/4 Sl+ 11:19 0:00 python csp_build.py
user 5373 0.0 0.0 8096 960 pts/6 S+ 11:20 0:00 grep python csp_build.py

[~]$ kill 5124

How can I extract the process id automatically and kill it in the same line?

Like this:
[~]$ ps aux | grep 'python csp_build.py' | kill <regex that returns the pid>

In bash, you should be able to do:
# kill $(ps aux | grep '[p]ython csp_build.py' | awk '{print $2}')

Details on its workings are as follows:
* The ps gives you the list of all the processes.
* The grep filters that based on your search string, [p] is a trick to stop you picking up the actual grep process itself.
* The awk just gives you the second field of each line, which is the PID.
* The $(x) construct means to execute x then take its output and put it on the command line. The output of that ps pipeline inside that construct above is the list of process IDs so you end up with a command like kill 1234 1122 7654.

2021年5月28日 星期五

[ Python 常見問題 ] How do I use itertools.groupby()?

 Source From Here

I haven't been able to find an understandable explanation of how to actually use Python's itertools.groupby() function. I've reviewed the documentation, but I've had trouble trying to apply them beyond a simple list of numbers. So, how do I use of itertools.groupby()? Is there another technique I should be using? Pointers to good "prerequisite" reading would also be appreciated.

The part I didn't get is that in the example construction:
  1. groups = []  
  2. uniquekeys = []  
  3. for k, g in groupby(data, keyfunc):  
  4.    groups.append(list(g))    # Store group iterator as a list  
  5.    uniquekeys.append(k)  
k is the current grouping key, and g is an iterator that you can use to iterate over the group defined by that grouping key. In other words, the groupby iterator itself returns iterators.

Here's an example of that, using clearer variable names:
  1. from itertools import groupby  
  3. things = [("animal""bear"), ("animal""duck"), ("plant""cactus"), ("vehicle""speed boat"), ("vehicle""school bus")]  
  5. for key, group in groupby(things, lambda x: x[0]):  
  6.     for thing in group:  
  7.         print("A %s is a %s." % (thing[1], key))  
  8.     print("")  
This will give you the output:
A bear is a animal.
A duck is a animal.

A cactus is a plant.

A speed boat is a vehicle.
A school bus is a vehicle.

In this example, things is a list of tuples where the first item in each tuple is the group the second item belongs to.

The groupby() function takes two arguments: (1) the data to group and (2) the function to group it with.

Here, lambda x: x[0] tells groupby() to use the first item in each tuple as the grouping key.

In the above for statement, groupby returns three (key, group iterator) pairs - once for each unique key. You can use the returned iterator to iterate over each individual item in that group. Here's a slightly different example with the same data, using a list comprehension:
  1. for key, group in groupby(things, lambda x: x[0]):  
  2.     listOfThings = " and ".join([thing[1for thing in group])  
  3.     print(key + "s:  " + listOfThings + ".")  
This will give you the output:
animals: bear and duck.
plants: cactus.
vehicles: speed boat and school bus.

2021年5月21日 星期五

[ Python 文章收集 ] How Python 3.9 fixed decorators and improved dictionaries

 Source From Here

This is the tenth in a series of articles about features that first appeared in a version of Python 3.x. Some of these versions have been out for a while. Python 3.9 was first released in 2020 with cool new features that are still underused. Here are three of them.

Adding dictionaries
Say you have a dictionary with "defaults," and you want to update it with parameters. Before Python 3.9, the best option was to copy the defaults dictionary and then use the .update() method. Python 3.9 introduced the union operator to dictionaries:
>>> defaults = dict(who="someone", where="somewhere")
>>> params = dict(where="our town", when="today")
>>> defaults | params
{'who': 'someone', 'where': 'our town', 'when': 'today'}

Note that the order matters. In this case, the where value from params overrides the default, as it should.
Removing prefixes
If you have done ad hoc text parsing or cleanup with Python, you will have written code like:
  1. def process_pricing_line(line):  
  2.     if line.startswith("pricing:"):  
  3.         return line[len("pricing:"):]  
  4.     return line  
  5. process_pricing_line("pricing:20")  

This kind of code is prone to errors. For example, if the string is copied incorrectly to the next line, the price will become 0 instead of 20, and it will happen silently. Since Python 3.9, strings have a .lstrip() method:
>>> "pricing:20".lstrip("pricing:")

>>> "20".lstrip("pricing:")

Arbitrary decorator expressions
Previously, the rules about which expressions are allowed in a decorator were underdocumented and hard to understand. For example, while:
  1. @item.thing  
  2. def foo():  
  3.     pass  
is valid, and:
  1. @item.thing()  
  2. def foo():  
  3.     pass  
is valid, the similar:
  1. @item().thing  
  2. def foo():  
  3.     pass  
produces a syntax error.

Starting in Python 3.9, any expression is valid as a decorator:
  1. from unittest import mock  
  3. item = mock.MagicMock()  
  5. @item().thing  
  6. def foo():  
  7.     pass  
  8. print(item.return_value.thing.call_args[0][0])  
<function foo at 0x7f3733897040>

2021年5月17日 星期一

[ Python 文章收集 ] 強大的 Python 任務自動化工具!invoke 十分鐘入門指南

 Source From Here

1. invoke 可以做什麼?
invoke 是從著名的遠程部署工具 Fabric 中分離出來的,它與 paramiko 一起是 Fabric 的兩大最核心的基礎組件。

除了作為命令行工具,它專注於 “任務執行”(task execution),可以標註和組織任務,並通過 CLI(command-line interface,即命令行界面) 和 shell 命令來執行任務。同樣是任務自動化工具,invoke 與 tox/nox 在側重點上有所不同:
* tox/nox 主要是在打包、測試、持續集成等方面的自動化(當然它們能做的還不止於此
* invoke 則更具普遍性,可以用在任何需要“執行任務”的場景,可以是無相關性的任務組,也可以是有順序依賴的分步驟的工作流

2. 怎麼使用invoke?
首先,安裝很簡單:pip install invoke。其次,簡單使用時有以下要素:
* 任務文件。創建一個 tasks.py 文件。
@task 裝飾器。在一個函數上添加 @task 裝飾器,即可將該函數標記為一個任務,接受 invoke 的調度管理。
* 上下文參數。給被裝飾的函數添加一個上下文參數(context argument),注意它必須作為第一個參數,而命名按約定可以是 c 或 ctx 或 context
* 命令行執行。在命令行中執行 invoke --list 來查看所有任務,運行 invoke xxx 來執行名為 xxx 的任務。命令行中的 “invoke” 可以簡寫成 “inv”。

  1. from invoke import task  
  3. @task  
  4. def hello(c):  
  5.     print("Hello world!")  
  8. @task  
  9. def greet(c, name):  
  10.     c.run(f"echo 'Nice to meet you, {name}'!")  
* hello 任務 調用了Python 內置的 print 函數,會打印一個字符串 “Hello world!”
* greet 任務 調用了 上下文參數 的 run() 方法,可以執行 shell 命令,同時本例中還可以接收一個參數。在 shell 命令中,echo 可理解成打印,所以這也是一個打印任務.

以上代碼寫在 tasks.py 文件中,首先導入裝飾器 from invoke import task@task 裝飾器可以不帶參數,也可以帶參數(參見下一節),被它裝飾了的函數就是一個任務。上下文參數(即上例的 “c)必須要顯式地指明,如果缺少這個參數,執行時會拋出異常:“TypeError: Tasks must have an initial Context argument!

然後在 tasks.py 文件的同級目錄中,打開命令行窗口,執行命令。如果執行的位置找不到這個任務文件,則會報錯:“Can't find any collection named 'tasks'!”. 正常情況下,通過執行 inv --list 或者 inv -l,可以看到所有任務的列表(按字母表順序排序):
# inv -l
Available tasks:


# inv hello
Hello world!

# inv greet
'greet' did not receive required positional arguments: 'name'

# inv greet John
Nice to meet you, John!

# inv greet --name='Peter'
Nice to meet you, Peter!

缺少傳參時,報錯:"'greet' did not receive required positional arguments: 'name'";多餘傳參時,報錯:"No idea what '???' is!"

3. 如何用好 invoke?
介紹完 invoke 的簡單用法,我們知道了它所需的幾項要素,也大致知道了它的使用步驟,接下來是它的其它用法。

3.1 添加幫助信息
在上例中,“inv -l” 只能看到任務名稱,缺少必要的輔助信息,為了加強可讀性,我們可以這樣寫:
  1. @task(help={'name''A param for test'})  
  2. def greet(c, name):  
  3.     """  
  4.     A test for shell command.  
  5.     Second line.  
  6.     """  
  7.     c.run(f"echo 'Nice to meet you, {name}'!")  
其中,文檔字符串的第一行內容會作為摘錄,在 “inv -l” 的查詢結果中展示,而且完整的內容與 @task 的 help 內容,會對應在 “inv --help” 中展示:
# inv -l
Available tasks:

greet A test for shell command.

# inv --help greet
  1. Usage: inv[oke] [--core-opts] greet [--options] [other tasks here ...]  
  3. Docstring:  
  4.   A test for shell command.  
  5.   Second line.  
  7. Options:  
  8.   -n STRING, --name=STRING   A param for test  

3.2 任務的分解與組合
* 對內分解,對外統一:只定義一個 @task 的任務,作為總體的任務入口,實際的處理邏輯可以抽象成多個方法,但是外部不感知到它們
* 多點呈現,單點匯總:定義多個 @task 的任務,外部可以感知並分別調用它們,同時將有關聯的任務組合起來,調用某個任務時,也執行其它相關聯的任務

第一種思路很容易理解,實現與使用都很簡單,但是其缺點是缺少靈活性,難於單獨執行其中的某個/些子任務。適用於相對獨立的單個任務,通常也不需要invoke 就能做到(使用 invoke 的好處是,擁有命令行的支持); 第二種思路更加靈活,既方便單一任務的執行,也方便多任務的組合執行。實際上,這種場景才是 invoke 發揮最大價值的場景。

那麼,invoke 如何實現分步任務的組合呢?可以在 @task 裝飾器的 “pre” 與 “post” 參數中指定,分別表示前置任務與後置任務:
  1. @task  
  2. def clean(c):  
  3.     c.run("echo clean")  
  5. @task  
  6. def message(c):  
  7.     c.run("echo message")  
  9. @task(pre=[clean], post=[message])  
  10. def build(c):  
  11.     c.run("echo build")  
clean 與 message 任務作為子任務,可以單獨調用,也可以作為 build 任務的前置與後置任務而組合使用:
# inv clean

# inv message

# inv build

這兩個參數是列表類型,即可設置多個任務。另外,在默認情況下,@task 裝飾器的位置參數會被視為前置任務,接著上述代碼,我們寫一個:
  1. @task(clean, message)  
  2. def test(c):  
  3.   c.run("echo test")  
然後執行,會發現 兩個參數都被視為了 前置任務
# inv test

3.3 模塊的拆分與整合
如果要管理很多相對獨立的大型任務,或者需要多個團隊分別維護各自的任務,那麼,就有必要對 tasks.py 作拆分與整合。

例如,現在有多份 tasks.py,彼此是相對完整而獨立的任務模塊,不方便把所有內容都放在一個文件中,那麼,如何有效地把它們整合起來管理呢?invoke 提供了這方面的支持。首先,只能保留一份名為 “tasks.py” 的文件,其次,在該文件中導入其它改名後的任務文件,最後,使用 invoke 的 Collection 類把它們關聯起來。我們把本文中第一個示例文件改名為 task1.py,並新建一個 tasks.py 文件,內容如下:
  1. from invoke import Collection, task  
  2. import task1  
  5. @task  
  6. def deploy(c):  
  7.   c.run("echo deploy")  
  10. namespace = Collection(task1, deploy)  
每個 py 文件擁有獨立的命名空間,而在此處,我們用 Collection 可以創建出一個新的命名空間,從而實現對所有任務的統一管理。效果如下:
# inv -l
Available tasks:

task1.greet A test for shell command.

# inv deploy

# inv task1.hello
Hello world!

關於不同任務模塊的導入、嵌套、混合、起別名等內容,還有不少細節,請查閱 官方文檔 (Constructing namespaces) 了解。

3.4 交互式操作

invoke 提供了在程序運行期的監控能力,可以監聽 stdout 和 stderr,並支持在 stdin 中輸入必要的信息。例如,假設某個任務(excitable-program)在執行時會提示 “Are you ready? [y/n]”,只有輸入了 “y” 並按下回車鍵,才會執行後續的操作。那麼,在代碼中指定 responses 參數的內容,只要監聽到匹配信息,程序會自動執行相應的操作:
  1. responses = {r"Are you ready? \[y/n\] ""y\n"}  
  2. ctx.run("excitable-program", responses=responses)  
responses 是字典類型,鍵值對分別為監聽內容及其回應內容。需注意,鍵值會被視為正則表達式,所以像本例中的方括號就要先轉義。

3.5 作為命令行工具庫
Python 中有不少好用的命令行工具庫,比如標準庫中的 argparse、Flask 作者開源的 click 與 谷歌 開源的 fire 等等,而 invoke 也可以作為命令行工具庫使用。

事實上,Fabric 項目最初把 invoke 分離成獨立的庫,就是想讓它承擔解析命令行與執行子命令的任務。所以,除了作為自動化任務管理工具,invoke 也可以被用於開發命令行工具。

官方文檔中給出了一個示例,我們可以了解到它的基本用法。假設我們要開發一個 tester工具,讓用戶 pip install tester 安裝,而此工具提供兩個執行命令:tester unit 和 tester intergration。這兩個子命令需要在 tasks.py 文件中定義:
  1. # tasks.py  
  2. from invoke import task  
  4. @task  
  5. def unit(c):  
  6.     print("Running unit tests!")  
  8. @task  
  9. def integration(c):  
  10.     print("Running integration tests!")  
  1. # main.py  
  2. from invoke import Collection, Program  
  3. from tester import tasks  
  5. program = Program(namespace=Collection.from_module(tasks), version='0.1.0')  
  1. # setup.py  
  2. setup(  
  3.     name='tester',  
  4.     version='0.1.0',  
  5.     packages=['tester'],  
  6.     install_requires=['invoke'],  
  7.     entry_points={  
  8.         'console_scripts': ['tester = tester.main:program.run']  
  9.     }  
  10. )  
$ tester --version
Tester 0.1.0

$ tester --help
  1. Usage: tester [--core-opts] <subcommand> [--subcommand-opts] ...  
  3. Core options:  
  4.   ... core options here, minus task-related ones ...  
  6. Subcommands:  
  7.   unit  
  8.   integration  

$ tester --list
No idea what '--list' is!

$ tester unit
Running unit tests!

上手容易,開箱即用,invoke 不失為一款可以考慮的命令行工具庫。更多詳細用法,請查閱文檔 (Reusing Invoke’s CLI module as a distinct binary)

[ Python 常見問題 ] When using unittest.mock.patch, why is autospec not True by default?

  Source From  Here Question When you patch a function using  mock , you have the option to specify  autospec  as True: If you set  autospec...