如何创建fabric2任务装饰器?

huangapple go评论101阅读模式
英文:

How to create fabric2 tasks decorators?

问题

以下是您要翻译的代码部分:

  1. 让我们首先考虑一些使用fabric2的未重构的工作代码片段
  2. import traceback import inspect
  3. from fabric2.tasks import task
  4. from fabric2 import Config, Connection
  5. def get_function_name():
  6. return traceback.extract_stack(None, 2)[0][2]
  7. def get_function_parameters_and_values():
  8. frame = inspect.currentframe().f_back
  9. args, _, _, values = inspect.getargvalues(frame)
  10. return ", ".join([str(i) for i in args])
  11. @task def foo1(c, arg1):
  12. print(f'<{get_function_name()} {get_function_parameters_and_values()}>)
  13. def _inner():
  14. if int(arg1) < 5:
  15. print('arg1 is not big enough')
  16. return
  17. print('doing foo1 stuff')
  18. res = _inner()
  19. print(f'</{get_function_name()}')
  20. return res
  21. @task def foo2(c, arg1, arg2):
  22. print(f'<{get_function_name()} {get_function_parameters_and_values()}>)
  23. def _inner():
  24. if int(arg1) < 5 and int(arg2) < 5:
  25. print('arg1 & arg2 are not big enough')
  26. return
  27. print('doing foo2 stuff')
  28. res = _inner()
  29. print(f'</{get_function_name()}')
  30. return res
  31. 现在如您所见上面存在一些代码重复我想通过包装器/装饰器的方式将其提取出来问题出在如果我尝试像这样做
  32. def wrap(func):
  33. def wrapped(*args, **kwargs):
  34. print('begin')
  35. res = func(*args, **kwargs)
  36. print('end')
  37. return wrapped
  38. @task
  39. @wrap
  40. def foo3(c, arg1):
  41. if int(arg1) < 5:
  42. print('arg1 is not big enough')
  43. return
  44. print('doing foo3 stuff')
  45. @wrap
  46. @task
  47. def foo4(c, arg1):
  48. if int(arg1) < 5:
  49. print('arg1 is not big enough')
  50. return
  51. print('doing foo4 stuff')
  52. 当我运行其中任何一个任务时都会出现异常例如
  53. (py364_32) D:\sources\personal\python\bplfab\examples>fab2 foo3 --arg1=3
  54. Traceback (most recent call last):
  55. File "d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py", line 160, in argspec
  56. context_arg = arg_names.pop(0)
  57. IndexError: pop from empty list
  58. 在处理上述异常时又出现了另一个异常
  59. Traceback (most recent call last):
  60. File "d:\software\python364_32\Lib\runpy.py", line 193, in _run_module_as_main
  61. "__main__", mod_spec)
  62. File "d:\software\python364_32\Lib\runpy.py", line 85, in _run_code
  63. exec(code, run_globals)
  64. File "D:\virtual_envs\py364_32\Scripts\fab2.exe\__main__.py", line 7, in <module>
  65. File "d:\virtual_envs\py364_32\lib\site-packages\invoke\program.py", line 352, in run
  66. self.parse_collection()
  67. File "d:\virtual_envs\py364_32\lib\site-packages\invoke\program.py", line 444, in parse_collection
  68. self.load_collection()
  69. File "d:\virtual_envs\py364_32\lib\site-packages\fabric2\main.py", line 87, in load_collection
  70. super(Fab, self).load_collection()
  71. File "d:\virtual_envs\py364_32\lib\site-packages\invoke\program.py", line 661, in load_collection
  72. module, parent = loader.load(coll_name)
  73. File "d:\virtual_envs\py364_32\lib\site-packages\invoke\loader.py", line 76, in load
  74. module = imp.load_module(name, fd, path, desc)
  75. File "<frozen importlib._bootstrap>", line 235, in load_module
  76. File "<frozen importlib._bootstrap>", line 172, in load_source
  77. File "D:\sources\personal\python\bplfab\examples\fabfile.py", line 59, in <module>
  78. @wrap
  79. File "d:\virtual_envs\py364_32\lib\site-packages\fabric2\tasks.py", line 71, in task
  80. return invoke.task(*args, **kwargs)
  81. File "d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py", line 313, in task
  82. return klass(args[0], **kwargs)
  83. File "d:\virtual_envs\py364_32\lib\site-packages\fabric2\tasks.py", line 21, in __init__
  84. super(Task, self).__init__(*args, **kwargs)
  85. File "d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py", line 77, in __init__
  86. self.positional = self.fill_implicit_positionals(positional)
  87. File "d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py", line 168, in fill_implicit_positionals
  88. args, spec_dict = self.argspec(self.body)
  89. File "d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py", line 163, in argspec
  90. raise TypeError("Tasks must have an initial Context argument!")
  91. TypeError: Tasks must have an initial Context argument!
  92. (py364_32) D:\sources\personal\python\bplfab\examples>fab2 foo4 --arg1=3
  93. Traceback (most recent call last):
  94. File "d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py", line 160, in argspec
  95. context_arg = arg_names.pop(0)
  96. IndexError: pop from empty list
  97. 在处理上述异常时又出现了另一个异常
  98. Traceback (most recent call last):
  99. File "d:\software\python364_32\Lib\runpy.py", line 193, in _run_module_as_main
  100. "__main__", mod_spec)
  101. File "d:\software\python364_32\Lib\runpy.py", line 85, in _run_code
  102. exec(code, run_globals)
  103. File "D:\virtual_envs\py364_32\Scripts\fab2.exe\__main__.py", line 7, in <module>
  104. File "d:\virtual_envs\py364_32\lib\site-packages\invoke\program.py", line 352, in run
  105. self.parse_collection()
  106. File "d:\virtual_envs\py364_32\lib\site-packages\invoke\program
  107. <details>
  108. <summary>英文:</summary>
  109. Let&#39;s start by considering some unrefactored working piece of code that uses fabric2:
  110. import traceback import inspect
  111. from fabric2.tasks import task
  112. from fabric2 import Config, Connection
  113. def get_function_name():
  114. return traceback.extract_stack(None, 2)[0][2]
  115. def get_function_parameters_and_values():
  116. frame = inspect.currentframe().f_back
  117. args, _, _, values = inspect.getargvalues(frame)
  118. return &quot;, &quot;.join([str(i) for i in args])
  119. @task def foo1(c, arg1):
  120. print(f&#39;&lt;{get_function_name()} {get_function_parameters_and_values()}&gt;&#39;)
  121. def _inner():
  122. if int(arg1) &lt; 5:
  123. print(&#39;arg1 is not big enough&#39;)
  124. return
  125. print(&#39;doing foo1 stuff&#39;)
  126. res = _inner()
  127. print(f&#39;&lt;/{get_function_name}&#39;)
  128. return res
  129. @task def foo2(c, arg1, arg2):
  130. print(f&#39;&lt;{get_function_name()} {get_function_parameters_and_values()}&gt;&#39;)
  131. def _inner():
  132. if int(arg1) &lt; 5 and int(arg2) &lt; 5:
  133. print(&#39;arg1 &amp; arg2 are not big enough&#39;)
  134. return
  135. print(&#39;doing foo2 stuff&#39;)
  136. res = _inner()
  137. print(f&#39;&lt;/{get_function_name}&#39;)
  138. return res
  139. Now, as you can see above, there is some code duplication I&#39;d like to factor out in the way of a wrapper/decorator... Problem comes if I try something like this:
  140. def wrap(func):
  141. def wrapped(*args, **kwargs):
  142. print(&#39;begin&#39;)
  143. res = func(*args, **kwargs)
  144. print(&#39;end&#39;)
  145. return wrapped
  146. @task
  147. @wrap
  148. def foo3(c, arg1):
  149. if int(arg1) &lt; 5:
  150. print(&#39;arg1 is not big enough&#39;)
  151. return
  152. print(&#39;doing foo3 stuff&#39;)
  153. @wrap
  154. @task
  155. def foo4(c, arg1):
  156. if int(arg1) &lt; 5:
  157. print(&#39;arg1 is not big enough&#39;)
  158. return
  159. print(&#39;doing foo4 stuff&#39;)
  160. And I run either of these tasks I&#39;ll get exceptions with both such as:
  161. (py364_32) D:\sources\personal\python\bplfab\examples&gt;fab2 foo3 --arg1=3
  162. Traceback (most recent call last):
  163. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py&quot;, line 160, in argspec
  164. context_arg = arg_names.pop(0)
  165. IndexError: pop from empty list
  166. During handling of the above exception, another exception occurred:
  167. Traceback (most recent call last):
  168. File &quot;d:\software\python364_32\Lib\runpy.py&quot;, line 193, in _run_module_as_main
  169. &quot;__main__&quot;, mod_spec)
  170. File &quot;d:\software\python364_32\Lib\runpy.py&quot;, line 85, in _run_code
  171. exec(code, run_globals)
  172. File &quot;D:\virtual_envs\py364_32\Scripts\fab2.exe\__main__.py&quot;, line 7, in &lt;module&gt;
  173. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\program.py&quot;, line 352, in run
  174. self.parse_collection()
  175. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\program.py&quot;, line 444, in parse_collection
  176. self.load_collection()
  177. File &quot;d:\virtual_envs\py364_32\lib\site-packages\fabric2\main.py&quot;, line 87, in load_collection
  178. super(Fab, self).load_collection()
  179. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\program.py&quot;, line 661, in load_collection
  180. module, parent = loader.load(coll_name)
  181. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\loader.py&quot;, line 76, in load
  182. module = imp.load_module(name, fd, path, desc)
  183. File &quot;d:\virtual_envs\py364_32\lib\imp.py&quot;, line 235, in load_module
  184. return load_source(name, filename, file)
  185. File &quot;d:\virtual_envs\py364_32\lib\imp.py&quot;, line 172, in load_source
  186. module = _load(spec)
  187. File &quot;&lt;frozen importlib._bootstrap&gt;&quot;, line 684, in _load
  188. File &quot;&lt;frozen importlib._bootstrap&gt;&quot;, line 665, in _load_unlocked
  189. File &quot;&lt;frozen importlib._bootstrap_external&gt;&quot;, line 678, in exec_module
  190. File &quot;&lt;frozen importlib._bootstrap&gt;&quot;, line 219, in _call_with_frames_removed
  191. File &quot;D:\sources\personal\python\bplfab\examples\fabfile.py&quot;, line 59, in &lt;module&gt;
  192. @wrap
  193. File &quot;d:\virtual_envs\py364_32\lib\site-packages\fabric2\tasks.py&quot;, line 71, in task
  194. return invoke.task(*args, **kwargs)
  195. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py&quot;, line 313, in task
  196. return klass(args[0], **kwargs)
  197. File &quot;d:\virtual_envs\py364_32\lib\site-packages\fabric2\tasks.py&quot;, line 21, in __init__
  198. super(Task, self).__init__(*args, **kwargs)
  199. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py&quot;, line 77, in __init__
  200. self.positional = self.fill_implicit_positionals(positional)
  201. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py&quot;, line 168, in fill_implicit_positionals
  202. args, spec_dict = self.argspec(self.body)
  203. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py&quot;, line 163, in argspec
  204. raise TypeError(&quot;Tasks must have an initial Context argument!&quot;)
  205. TypeError: Tasks must have an initial Context argument!
  206. (py364_32) D:\sources\personal\python\bplfab\examples&gt;fab2 foo4 --arg1=3
  207. Traceback (most recent call last):
  208. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py&quot;, line 160, in argspec
  209. context_arg = arg_names.pop(0)
  210. IndexError: pop from empty list
  211. During handling of the above exception, another exception occurred:
  212. Traceback (most recent call last):
  213. File &quot;d:\software\python364_32\Lib\runpy.py&quot;, line 193, in _run_module_as_main
  214. &quot;__main__&quot;, mod_spec)
  215. File &quot;d:\software\python364_32\Lib\runpy.py&quot;, line 85, in _run_code
  216. exec(code, run_globals)
  217. File &quot;D:\virtual_envs\py364_32\Scripts\fab2.exe\__main__.py&quot;, line 7, in &lt;module&gt;
  218. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\program.py&quot;, line 352, in run
  219. self.parse_collection()
  220. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\program.py&quot;, line 444, in parse_collection
  221. self.load_collection()
  222. File &quot;d:\virtual_envs\py364_32\lib\site-packages\fabric2\main.py&quot;, line 87, in load_collection
  223. super(Fab, self).load_collection()
  224. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\program.py&quot;, line 661, in load_collection
  225. module, parent = loader.load(coll_name)
  226. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\loader.py&quot;, line 76, in load
  227. module = imp.load_module(name, fd, path, desc)
  228. File &quot;d:\virtual_envs\py364_32\lib\imp.py&quot;, line 235, in load_module
  229. return load_source(name, filename, file)
  230. File &quot;d:\virtual_envs\py364_32\lib\imp.py&quot;, line 172, in load_source
  231. module = _load(spec)
  232. File &quot;&lt;frozen importlib._bootstrap&gt;&quot;, line 684, in _load
  233. File &quot;&lt;frozen importlib._bootstrap&gt;&quot;, line 665, in _load_unlocked
  234. File &quot;&lt;frozen importlib._bootstrap_external&gt;&quot;, line 678, in exec_module
  235. File &quot;&lt;frozen importlib._bootstrap&gt;&quot;, line 219, in _call_with_frames_removed
  236. File &quot;D:\sources\personal\python\bplfab\examples\fabfile.py&quot;, line 59, in &lt;module&gt;
  237. @wrap
  238. File &quot;d:\virtual_envs\py364_32\lib\site-packages\fabric2\tasks.py&quot;, line 71, in task
  239. return invoke.task(*args, **kwargs)
  240. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py&quot;, line 313, in task
  241. return klass(args[0], **kwargs)
  242. File &quot;d:\virtual_envs\py364_32\lib\site-packages\fabric2\tasks.py&quot;, line 21, in __init__
  243. super(Task, self).__init__(*args, **kwargs)
  244. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py&quot;, line 77, in __init__
  245. self.positional = self.fill_implicit_positionals(positional)
  246. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py&quot;, line 168, in fill_implicit_positionals
  247. args, spec_dict = self.argspec(self.body)
  248. File &quot;d:\virtual_envs\py364_32\lib\site-packages\invoke\tasks.py&quot;, line 163, in argspec
  249. raise TypeError(&quot;Tasks must have an initial Context argument!&quot;)
  250. TypeError: Tasks must have an initial Context argument!
  251. It seems there is already an opened issue related somehow to this https://github.com/pyinvoke/invoke/issues/555 but so far I haven&#39;t been able to find a workaround for this.
  252. Could you explain me what I&#39;m doing wrong here? But the most important, could you provide a way to workaround this fabric2 &quot;limitation&quot; and being able to create a decorator that allows me to use inspect to capture the task&#39;s name as well as the arguments passed in the command line?
  253. Thanks in advance.
  254. </details>
  255. # 答案1
  256. **得分**: 1
  257. Custom decorators必须具有一个固定的初始上下文参数就像异常所说的那样所以`func(c, *args, **kwargs)`有效`func(*args, **kwargs)`无效
  258. ```py
  259. def trace(func):
  260. @functools.wraps(func)
  261. def wrapper(c, *args, **kwargs):
  262. print(f'开始 {func.__name__}')
  263. func(c, *args, **kwargs)
  264. print(f'结束 {func.__name__}')
  265. return wrapper
  266. @task
  267. @trace
  268. def uname(c):
  269. c.run('uname -a')
英文:

Custom decorators must have a fixed initial context argument as the exception says, so func(c, *args, **kwargs) works while func(*args, **kwargs) doesn't:

  1. def trace(func):
  2. @functools.wraps(func)
  3. def wrapper(c, *args, **kwargs):
  4. print(f&#39;Begin {func.__name__}&#39;)
  5. func(c, *args, **kwargs)
  6. print(f&#39;End {func.__name__}&#39;)
  7. return wrapper
  8. @task
  9. @trace
  10. def uname(c):
  11. c.run(&#39;uname -a&#39;)

huangapple
  • 本文由 发表于 2020年1月3日 23:00:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/59580785.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定