python提供了bumpversion工具,可以极简的管理项目的版本。
准备
pip install bumpversion
1
在项目根目录下新建.bumpversion.cfg,内容可以参考:
[bumpversion]
current_version = 0.9.1
commit = True
tag = False
[bumpversion:file:VERSION]
1
2
3
4
5
6
跟目录下,新建VERSION文件,内容为
0.9.1
使用实例
bumpversion patch
1
则VERSION文件自动变成0.9.2,同时自动git commit。
参考
https://pypi.python.org/pypi/bumpversion
为什么用bumpversion
上述例子太简单,以至于很多人觉得可以自己手动改数字。
问题是,实际项目的版本分布在多个文件,例如setup.py, docs/xxx文件等等,人工一个个修改,易错且浪费时间。
bumpversion可以一句话搞定!
————————————————
版权声明:本文为CSDN博主「mania_yan」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yyw794/java/article/details/79892042
from : https://blog.csdn.net/yyw794/article/details/79892042
Tuesday, 28 April 2020
Saturday, 25 April 2020
Python Convert String to Boolean
今天在撰寫 AWS Lambda 時因為從環境變數讀取設定的關係,需要把字串轉成布林來做判斷,一般的想法不外乎 Python 會幫你轉換,或是用 bool(“True”) 轉換就可以,首先其實 Python 是不會幫你轉換的,這點很重要! Python 在形態方面其實是不會做自動轉換的,他只會很阿莎力的報錯,不過大家肯定想說用 bool() 轉換就沒問題了吧!? 事實上,這個地雷害我今天搞了半小時才解決…
事實上在 Python 裡面,你不管喂什麼給 bool() 他都只會回傳 True,只有 bool(0) 的時候是 False,包括以下這些情況都是回傳 True 哦:
bool("True") bool("false") bool("False") bool("0")
是的別懷疑,你不管喂什麼給他,幾乎都只會獲得 True,一不小心把它放進條件裡面就會造成特異現象。
如果你想要把字串轉換成布林的話,建議使用 distutils.util 提供的 strtobool ,這是 Python 內建的模組,功能蠻強大的,可以參考下面的範例
from distutils.util import strtobool strtobool("True") strtobool("Yes") strtobool("Y") strtobool("true")
以上這些都可以正確轉換成 True 哦,根本就是神器阿!!!
from : https://coder.tw/?p=7017
Friday, 24 April 2020
Ignoring empty files from coverage report
As of coverage.py 4.0, the
coverage report --skip-covered --rcfile=/path/to/.coveragerc -m
from : http://www.hackerav.com/?post=237009
--skip-covered
option is available to do exactlycoverage report --skip-covered --rcfile=/path/to/.coveragerc -m
from : http://www.hackerav.com/?post=237009
Documentation - Mock & Testfixtures
Testfixtures is a collection of helpers and mock objects that are useful when writing automated tests in Python.
from : https://testfixtures.readthedocs.io/en/latest/index.html
from : https://testfixtures.readthedocs.io/en/latest/index.html
PynamoDB
PynamoDB was written from scratch to be Pythonic, and supports the entire DynamoDB API.
from : https://pynamodb.readthedocs.io/en/latest/quickstart.html
from : https://pynamodb.readthedocs.io/en/latest/quickstart.html
bumpversion
Version-bump your software with a single command!
A small command line tool to simplify releasing software by updating all version strings in your source code by the correct increment. Also creates commits and tags:
- version formats are highly configurable
- works without any VCS, but happily reads tag information from and writes commits and tags to Git and Mercurial if available
- just handles text files, so it's not specific to any programming language
from : https://github.com/peritus/bumpversion/blob/master/README.rst
Python的import陷阱
在脫離Python幼幼班準備建立稍大型的專案的時候,學習如何組織化你的Python專案是一大要點。Python提供的module(模組)與package(套件)是建立架構的基本元件,但在module之間為了重複使用一些function(函數)或class(類別)而必須互相import(匯入),使用上一個不注意就會掉入混亂的import陷阱。
此篇將會從基本module和package介紹起,提點基本import語法及absolute import和relative import的用法與差異,最後舉出幾個常見因為錯誤import觀念造成的錯誤。
* 請注意,以下只針對Python3進行講解與測試。
Module與Package
基本上一個file就是一個module,裡頭可以定義function,class,和variable。
把一個module想成一個file,那一個package就是一個folder了。Package可裝有subpackage和module,讓你的專案更條理更組織化,最後一坨打包好還能分給別人使用。
把一個module想成一個file,那一個package就是一個folder了。Package可裝有subpackage和module,讓你的專案更條理更組織化,最後一坨打包好還能分給別人使用。
先看看module。假設有一個module
sample_module.py
裡頭定義了一個function sample_func
:def sample_func():
print('Hello!')
現在你在同一個目錄裡下有另一個module
sample_module_import.py
想要重複使用這個function,這時可以直接從 sample_module
import拿取:from sample_module import sample_func
if __name__ == '__main__':
sample_func()
跑
python3 sample_module_import.py
會得到:Hello!
再來是package。我們把上面兩個檔案包在一個新的folder
sample_package
底下:sample_package/
├── __init__.py
├── sample_module.py
└── sample_module_import.py
很重要的是新增那個
__init__.py
檔。它是空的沒關係,但一定要有,有點宣稱自己是一個package的味道。
這時候如果是進到
sample_package
裡面跑一樣的指令,那沒差。但既然都打包成package了,通常是需要在package以外的地方下指令的,這時候裡面的import就要稍微做因應。
假設這時我們在跟
sample_package
同一個folder底下,讓我們修正一下 sample_package/sample_module_import.py
:'''跑在`sample_package`底下(非常見情況)'''
# from sample_module import sample_func
'''跑在跟`sample_package`同folder底下'''
from .sample_module import sample_func
'''或'''
# from sample_package.sample_module import sample_func
if __name__ == '__main__':
sample_func()
這邊兩種解來自於absolute import和relative import的選擇,等等再提。總之我們可以跑了:
$ python3 -m sample_package.sample_module_import
修好了。這邊
-m
是為了讓Python先import你要的package或module給你,然後再執行script。所以這邊 sample_module_import
在跑的時候,是以 sample_package
為環境的,這樣那些import才不會出錯。基本import語法
前面有看過了,這邊統整介紹一下。如果你想使用在其他module裡定義的function、class、variable等等,就需要在使用它們之前先進行import。通常都會把需要import的module們列在整個file的最一開始,但不是必須。
'''語法1:import [module]'''
# Import整個`random`module
import random
# 使用`random`module底下的`randint`function
print(random.randint(0, 5))
'''語法2:from [module] import [name1, name2, ...]'''
# 從`random`module裡import其中一個function`randint`
from random import randint
# 不一樣的是,使用`randint`的時候就不需要先寫`random`了
print(randint(0, 5))
'''語法3:import [module] as [new_name]'''
# Import整個`random`module,但這個名字可能跟其他地方有衝突,改名成`rd`
import random as rd
# 使用`rd`這個名稱取代原本的`random`
print(rd.randint(0, 5))
'''語法4(不推薦):from [module] import *'''
# Import所有`random`module底下的東西
from random import *
# 使用`randint`的時候也不需要先寫`random`
print(randint(0, 5))
語法4不推薦原因是容易造成名稱衝突,降低可讀性和可維護性。
Absolute Import v.s. Relative Import
Python有兩種import方法,absolute import及relative import。Absolute import就是完整使用module路徑,relative import則是使用以當前package為參考的相對路徑。Relative import的需求在於,有時候在改變專案架構的時候,裡面的package和module會拉來拉去,這時候如果這些package裡面使用的是relative import的話,他們的相對關係就不會改變,也就是不需要再一一進入module裡更改路徑。但因為relative import的路徑取決於當前package,所以在哪裡執行就會造成不一樣的結果,一不小心又要噴一堆error;這時absolute import就會減少許多困擾。
這邊參考PEP328提供的範例。Package架構如下:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
現在假設
package/subpackage1/moduleX.py
想要從其他module裡import一些東西,則使用下列語法:'''[A]表Absolute import範例;[R]表Relative import範例'''
# Import同一個package底下的sibling module `moduleY`
[A] from package.subpackage1 import moduleY
[R] from . import moduleY
[Error] import .moduleY
# 從同一個package底下的sibling module `moduleY` import `spam`這個function
[A] from package.subpackage1.moduleY import spam
[R] from .moduleY import spam
# 從隔壁package底下的module `moduleZ` import `eggs`這個function
[A] from package.subpackage2.moduleZ import eggs
[R] from ..subpackage2.moduleZ import eggs
# Import parent package底下的module `moduleA`
[A] from package import moduleA
[R] from .. import moduleA 或 from ...package import moduleA
要點:
- Relative import裡,
.
代表上一層,幾個.
就代表上幾層。 - Relative import一律採用
from ... import ...
語法,即使是從.
import也要寫from . import some_module
而非import .some_module
。原因是.some_module
這個名稱在expression裡無法出現。Absolute import則無限制。
常見import陷阱
Circular Import
想像一個module
A
在一開始要import另一個module B
裡的東西,但在匯入module B
的途中必須先執行它,而很不巧的它也需要從module A
import一些東西。但module A
還正在執行途中,自己都還沒定義好自己的function啊!於是你不讓我我不讓你,這種類似deadlock的情形正是常見的circular import(循環匯入)。
讓我們看看範例。現在在
sample_package
裡有 A
和 B
兩個module想互打招呼,程式碼分別如下:# A.py
from .B import B_greet_back
def A_say_hello():
print('A says hello!')
B_greet_back()
def A_greet_back():
print('A says hello back!')
if __name__ == '__main__':
A_say_hello()
# B.py
from .A import A_greet_back
def B_say_hello():
print('B says hello!')
A_greet_back()
def B_greet_back():
print('B says hello back!')
if __name__ == '__main__':
B_say_hello()
內容都一樣,只是
A/B
互換。B
很有禮貌想先打招呼。在與 sample_package
同目錄底下執行:$ python3 -m sample_package.B
>>>
Traceback (most recent call last):
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/path/to/sample_package/B.py", line 2, in <module>
from .A import A_greet_back
File "/path/to/sample_package/A.py", line 1, in <module>
from .B import B_greet_back
File "/path/to/sample_package/B.py", line 2, in <module>
from .A import A_greet_back
ImportError: cannot import name 'A_greet_back'
觀察到了嗎?
B
試圖import A_greet_back
,但途中先進到 A
執行,而因為Python是從頭開始一行一行執行下來的,於是在定義 A_greet_back
之前會先碰到自己的import statement,於是又進入 B
,然後陷入死胡同。
常見解決這種circular import的方法如下:
- Import整個module而非單一attribute把
B.py
更改成如下:# from .A import A_greet_back from . import A def B_say_hello(): print('B says hello!') # A_greet_back() A.A_greet_back() ...
就不會發生錯誤:$ python3 -m sample_package.B >>> B says hello! A says hello back!
理由是,執行from .A import A_greet_back
被迫要從load進來的A
module object中找出A_greet_back
的定義,但此時這個module object還是空的;而from . import A
就只會檢查A
module object 存不存在,至於A_greet_back
存不存在等到需要執行的時候再去找就行了。 - 延遲import把
B.py
更改成如下:# 前面全刪 def B_say_hello(): from .A import A_greet_back print('B says hello!') A_greet_back() ...
也會成功跑出結果。跟前面類似,Python在跑到這行時才會importA
module,這時因為B
module都已經load完了,所以不會有circular import的問題。但這個方法比較hacky一點,大概只能在hackathon中使用,否則正式專案裡看到這種難維護的code可能會有生命危險。另一方面,把所有import statement擺到整個module最後面也是類似效果,但也會被打。 - 好好釐清架構,避免circular import是的,治本方法還是好好思考自己寫的code為什麼會陷入這種危機,然後重新refactor吧。
Relative Import above Top-level Package
還不熟悉relative import的人常常會見到這個error:
ValueError: attempted relative import beyond top-level package
讓我們重現一下這個error。把
B.py
前頭更改成如下:# from . import A
from ..sample_package import A
...
現在我們的路徑位置在與
sample_package
同目錄底下。跑:$ python3 -m sample_package.B
>>>
Traceback (most recent call last):
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/path/to/sample_package/B.py", line 5, in <module>
from ..sample_package import A
ValueError: attempted relative import beyond top-level package
所謂的
top-level package
就是你所執行的package中最高的那一層,也就是 sample_package
。超過這一層的relative import是不被允許的,指的就是 ..sample_package
這行嘗試跳兩層上去而超過 sample_package
了。
可以試試更改當前目錄到上一層(
cd ..
),假設叫 parent_folder
,然後執行 python3 -m parent_folder.sample_package.B
,就會發現error消失了,因為現在的 top-level package
已經變成 parent_folder
了。結語
Import是各大語言必備功能,看似簡單,使用上來說陷阱卻頗多。如果搞不清楚Python中的import是怎麼運作的,除了在整體專案架構上難以靈活設計,更可能要陷入可怕的error海了。
我寫了一些額外的sample code放上github了,有不清楚的地方可以直接參考。
from : https://pyliaorachel.github.io/blog/tech/python/2017/09/15/pythons-import-trap.html
how can pylint msg “too many local variables” be disabled
you can modify the
max-locals
for local variable and max-args
for method arguments in .pylintrc file[DESIGN]
# Maximum number of locals for function / method body
max-locals=25
# Maximum number of arguments for function / method
max-args=10
from : https://stackoverflow.com/questions/50229140/how-can-pylint-msg-too-many-local-variables-be-disabled
Great plugin - Indent Rainbow
A simple extension to make indentation more readable
This extension colorizes the indentation in front of your text alternating four different colors on each step.
from : https://plugins.jetbrains.com/plugin/13308-indent-rainbow
Thursday, 23 April 2020
patch multiple methods from different modules (using Python mock)
The short answer is no you cannot use
patch.multiple()
to do it. As described in patch.multiple all arguments will be applied to all created mocks and all arguments MUST be attribute of the same object. You MUST do it one of the time by single patch calls.
Unfortunately you are using python 2.6 so you can use just
nested
fron contextlib
like pointed in python: create a "with" block on several context managers and Multiple context `with` statement in Python 2.6.
Maybe the more cleaner and simple way to do it is use
@patch
as decorator:@patch("foo.load",side_effects=["a","b","c"])
@patch("bar.check",return_value=True)
def test_mytest(mock_check,mock_load):
take_action()
assert mock_load.called
assert mock_check.called
If you need it in all tests of a test class you can decorate the class and use the mocks in all test methods:
@patch("foo.load",side_effects=["a","b","c"])
@patch("bar.check",return_value=True)
class TestMyTest(unittest.TestCase)
def test_mytestA(self,mock_check,mock_load):
take_action()
self.assertTrue(mock_load.called)
self.assertTrue(mock_check.called)
def test_mytestA(self,mock_check,mock_load):
mock_check.return_value = False
take_action()
self.assertTrue(mock_load.called)
self.assertTrue(mock_check.called)
Finally you can do it by using
with
and contextlib
and the first example become:from contextlib import nested
with nested(patch("foo.load",side_effects=["a","b","c"]), patch("bar.check",return_value=True)) as (mock_load, mock_check):
take_action()
assert mock_load.called
assert mock_check.called
... Or nest it by hand ....
with patch("foo.load",side_effects=["a","b","c"]) as mock_load:
with patch("bar.check",return_value=True)) as mock_check:
take_action()
assert mock_load.called
assert mock_check.called
My feel is that decorators are the most readable and simple to use.
from : https://stackoverflow.com/questions/27270958/patch-multiple-methods-from-different-modules-using-python-mock
Subscribe to:
Posts (Atom)