Skip to content

Commit 46b2009

Browse files
committed
Rework exceptions
1. Allow substitute `CalledProcessError` 2. Modify exceptions class hierarchy 3. Expose **kwargs in stable places 4. Temporary disable black formatter: due to bug it's destroy py35 Signed-off-by: Alexey Stepanov <penguinolog@gmail.com>
1 parent b0bad60 commit 46b2009

File tree

17 files changed

+281
-127
lines changed

17 files changed

+281
-127
lines changed

.pylintrc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ enable=old-style-class,
137137
old-ne-operator,
138138
old-octal-literal,
139139
import-star-module-level,
140-
c-extension-no-member,
141140
lowercase-l-suffix,
142141
deprecated-module,
143142
invalid-encoded-data,

.travis.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,13 @@ jobs:
9999
- pip install --upgrade pydocstyle
100100
script:
101101
- pydocstyle exec_helpers
102-
- <<: *code_style_check
103-
name: "Black formatting"
104-
install:
105-
- *upgrade_python_toolset
106-
- pip install --upgrade black
107-
script:
108-
- black --check exec_helpers
102+
# - <<: *code_style_check
103+
# name: "Black formatting"
104+
# install:
105+
# - *upgrade_python_toolset
106+
# - pip install --upgrade black
107+
# script:
108+
# - black --check exec_helpers
109109

110110
- stage: test
111111
<<: *python35

MANIFEST.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ include *.rst LICENSE requirements.txt
22
global-exclude *.c
33
exclude Makefile
44
prune tools
5-
exclude .travis.yml appveyor.yml
5+
exclude .travis.yml appveyor.yml azure-pipelines.yml
66
exclude tox.ini pytest.ini .coveragerc
77
prune test
88
prune .github
9+
prune .azure_pipelines
910
prune docs
1011
exclude CODEOWNERS CODE_OF_CONDUCT.md _config.yml

README.rst

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ This methods are almost the same for `SSHCleint` and `Subprocess`, except specif
149149
error_info=None, # type: typing.Optional[str]
150150
expected=None, # type: typing.Optional[typing.Iterable[int]]
151151
raise_on_err=True, # type: bool
152+
# Keyword only:
153+
exception_class=CalledProcessError, # typing.Type[CalledProcessError]
152154
**kwargs
153155
)
154156
@@ -160,6 +162,9 @@ This methods are almost the same for `SSHCleint` and `Subprocess`, except specif
160162
timeout=1 * 60 * 60, # type: type: typing.Union[int, float, None]
161163
error_info=None, # type: typing.Optional[str]
162164
raise_on_err=True, # type: bool
165+
# Keyword only:
166+
expected=None, # typing.Optional[typing.Iterable[typing.Union[int, ExitCodes]]]
167+
exception_class=CalledProcessError, # typing.Type[CalledProcessError]
163168
)
164169
165170
If no STDOUT or STDERR required, it is possible to disable this FIFO pipes via `**kwargs` with flags `open_stdout=False` and `open_stderr=False`.
@@ -208,7 +213,7 @@ SSHClient specific
208213
------------------
209214

210215
SSHClient commands support get_pty flag, which enables PTY open on remote side.
211-
PTY width and height can be set via kwargs, dimensions in pixels are always 0x0.
216+
PTY width and height can be set via keyword arguments, dimensions in pixels are always 0x0.
212217

213218
Possible to call commands in parallel on multiple hosts if it's not produce huge output:
214219

@@ -219,7 +224,9 @@ Possible to call commands in parallel on multiple hosts if it's not produce huge
219224
command, # type: str
220225
timeout=1 * 60 * 60, # type: type: typing.Union[int, float, None]
221226
expected=None, # type: typing.Optional[typing.Iterable[int]]
222-
raise_on_err=True # type: bool
227+
raise_on_err=True, # type: bool
228+
# Keyword only:
229+
exception_class=ParallelCallProcessError # typing.Type[ParallelCallProcessError]
223230
)
224231
results # type: typing.Dict[typing.Tuple[str, int], exec_result.ExecResult]
225232
@@ -237,7 +244,10 @@ For execute through SSH host can be used `execute_through_host` method:
237244
target_port=22, # type: int
238245
timeout=1 * 60 * 60, # type: type: typing.Union[int, float, None]
239246
verbose=False, # type: bool
247+
# Keyword only:
240248
get_pty=False, # type: bool
249+
width=80, # type: int
250+
height=24 # type: int
241251
)
242252
243253
Where hostname is a target hostname, auth is an alternate credentials for target host.
@@ -312,7 +322,7 @@ Additional (non-standard) helpers:
312322

313323
Subprocess specific
314324
-------------------
315-
Kwargs set properties:
325+
Keyword arguments:
316326

317327
- cwd - working directory.
318328
- env - environment variables dict.

doc/source/SSHClient.rst

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ API: SSHClient and SSHAuth.
121121
.. Note:: Enter and exit ssh context manager is produced as well.
122122
.. versionadded:: 1.2.1
123123

124-
.. py:method:: execute_async(command, stdin=None, open_stdout=True, open_stderr=True, verbose=False, log_mask_re=None, **kwargs)
124+
.. py:method:: execute_async(command, stdin=None, open_stdout=True, open_stderr=True, verbose=False, log_mask_re=None, *, get_pty=False, width=80, height=24, **kwargs)
125125
126126
Execute command in async mode and return channel with IO objects.
127127

@@ -138,12 +138,19 @@ API: SSHClient and SSHAuth.
138138
:param log_mask_re: regex lookup rule to mask command for logger.
139139
all MATCHED groups will be replaced by '<*masked*>'
140140
:type log_mask_re: typing.Optional[str]
141+
:param get_pty: Get PTY for connection
142+
:type get_pty: bool
143+
:param width: PTY width
144+
:type width: int
145+
:param height: PTY height
146+
:type height: int
141147
:rtype: SshExecuteAsyncResult
142148

143149
.. versionchanged:: 1.2.0 open_stdout and open_stderr flags
144150
.. versionchanged:: 1.2.0 stdin data
145151
.. versionchanged:: 1.2.0 get_pty moved to `**kwargs`
146152
.. versionchanged:: 2.1.0 Use typed NamedTuple as result
153+
.. versionchanged:: 3.2.0 Expose pty options as optional keyword-only arguments
147154

148155
.. py:method:: execute(command, verbose=False, timeout=1*60*60, **kwargs)
149156
@@ -160,7 +167,7 @@ API: SSHClient and SSHAuth.
160167

161168
.. versionchanged:: 1.2.0 default timeout 1 hour
162169

163-
.. py:method:: check_call(command, verbose=False, timeout=1*60*60, error_info=None, expected=None, raise_on_err=True, **kwargs)
170+
.. py:method:: check_call(command, verbose=False, timeout=1*60*60, error_info=None, expected=None, raise_on_err=True, *, exception_class=CalledProcessError, **kwargs)
164171
165172
Execute command and check for return code.
166173

@@ -176,13 +183,16 @@ API: SSHClient and SSHAuth.
176183
:type expected: ``typing.Optional[typing.Iterable[int]]``
177184
:param raise_on_err: Raise exception on unexpected return code
178185
:type raise_on_err: ``bool``
186+
:param exception_class: Exception class for errors. Subclass of CalledProcessError is mandatory.
187+
:type exception_class: typing.Type[CalledProcessError]
179188
:rtype: ExecResult
180189
:raises ExecHelperTimeoutError: Timeout exceeded
181190
:raises CalledProcessError: Unexpected exit code
182191

183192
.. versionchanged:: 1.2.0 default timeout 1 hour
193+
.. versionchanged:: 3.2.0 Exception class can be substituted
184194

185-
.. py:method:: check_stderr(command, verbose=False, timeout=1*60*60, error_info=None, raise_on_err=True, **kwargs)
195+
.. py:method:: check_stderr(command, verbose=False, timeout=1*60*60, error_info=None, raise_on_err=True, *, expected=None, exception_class=CalledProcessError, **kwargs)
186196
187197
Execute command expecting return code 0 and empty STDERR.
188198

@@ -196,14 +206,18 @@ API: SSHClient and SSHAuth.
196206
:type error_info: ``typing.Optional[str]``
197207
:param raise_on_err: Raise exception on unexpected return code
198208
:type raise_on_err: ``bool``
209+
:param expected: expected return codes (0 by default)
210+
:type expected: typing.Optional[typing.Iterable[typing.Union[int, ExitCodes]]]
211+
:param exception_class: Exception class for errors. Subclass of CalledProcessError is mandatory.
212+
:type exception_class: typing.Type[CalledProcessError]
199213
:rtype: ExecResult
200214
:raises ExecHelperTimeoutError: Timeout exceeded
201215
:raises CalledProcessError: Unexpected exit code or stderr presents
202216

203-
.. note:: expected return codes can be overridden via kwargs.
204217
.. versionchanged:: 1.2.0 default timeout 1 hour
218+
.. versionchanged:: 3.2.0 Exception class can be substituted
205219

206-
.. py:method:: execute_through_host(hostname, command, auth=None, target_port=22, verbose=False, timeout=1*60*60, get_pty=False, **kwargs)
220+
.. py:method:: execute_through_host(hostname, command, auth=None, target_port=22, verbose=False, timeout=1*60*60, *, get_pty=False, width=80, height=24, **kwargs)
207221
208222
Execute command on remote host through currently connected host.
209223

@@ -221,12 +235,18 @@ API: SSHClient and SSHAuth.
221235
:type timeout: ``typing.Union[int, float, None]``
222236
:param get_pty: open PTY on target machine
223237
:type get_pty: ``bool``
238+
:param width: PTY width
239+
:type width: int
240+
:param height: PTY height
241+
:type height: int
224242
:rtype: ExecResult
225243
:raises ExecHelperTimeoutError: Timeout exceeded
226244

227245
.. versionchanged:: 1.2.0 default timeout 1 hour
246+
.. versionchanged:: 3.2.0 Expose pty options as optional keyword-only arguments
247+
.. versionchanged:: 3.2.0 Exception class can be substituted
228248

229-
.. py:classmethod:: execute_together(remotes, command, timeout=1*60*60, expected=None, raise_on_err=True, **kwargs)
249+
.. py:classmethod:: execute_together(remotes, command, timeout=1*60*60, expected=None, raise_on_err=True, *, exception_class=ParallelCallProcessError, **kwargs)
230250
231251
Execute command on multiple remotes in async mode.
232252

@@ -240,12 +260,15 @@ API: SSHClient and SSHAuth.
240260
:type expected: ``typing.Optional[typing.Iterable[]]``
241261
:param raise_on_err: Raise exception on unexpected return code
242262
:type raise_on_err: ``bool``
263+
:param exception_class: Exception to raise on error. Mandatory subclass of ParallelCallProcessError
264+
:type exception_class: typing.Type[ParallelCallProcessError]
243265
:return: dictionary {(hostname, port): result}
244266
:rtype: typing.Dict[typing.Tuple[str, int], ExecResult]
245267
:raises ParallelCallProcessError: Unexpected any code at lest on one target
246268
:raises ParallelCallExceptions: At lest one exception raised during execution (including timeout)
247269

248270
.. versionchanged:: 1.2.0 default timeout 1 hour
271+
.. versionchanged:: 3.2.0 Exception class can be substituted
249272

250273
.. py:method:: open(path, mode='r')
251274

doc/source/Subprocess.rst

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@ API: Subprocess
88
99
.. py:class:: Subprocess()
1010
11-
.. py:method:: __init__(logger, log_mask_re=None)
11+
.. py:method:: __init__(logger, log_mask_re=None, *, logger=logging.getLogger("exec_helpers.subprocess_runner"))
1212
1313
ExecHelper global API.
1414

1515
:param log_mask_re: regex lookup rule to mask command for logger. all MATCHED groups will be replaced by '<*masked*>'
1616
:type log_mask_re: typing.Optional[str]
17+
:param logger: logger instance to use
18+
:type logger: logging.Logger
1719

1820
.. versionchanged:: 1.2.0 log_mask_re regex rule for masking cmd
1921
.. versionchanged:: 3.1.0 Not singleton anymore. Only lock is shared between all instances.
22+
.. versionchanged:: 3.2.0 Logger can be enforced.
2023

2124
.. py:attribute:: log_mask_re
2225
@@ -40,7 +43,7 @@ API: Subprocess
4043

4144
.. versionchanged:: 1.1.0 release lock on exit
4245

43-
.. py:method:: execute_async(command, stdin=None, open_stdout=True, open_stderr=True, verbose=False, log_mask_re=None, **kwargs)
46+
.. py:method:: execute_async(command, stdin=None, open_stdout=True, open_stderr=True, verbose=False, log_mask_re=None, *, cwd=None, env=None, **kwargs)
4447
4548
Execute command in async mode and return Popen with IO objects.
4649

@@ -57,11 +60,16 @@ API: Subprocess
5760
:param log_mask_re: regex lookup rule to mask command for logger.
5861
all MATCHED groups will be replaced by '<*masked*>'
5962
:type log_mask_re: ``typing.Optional[str]``
63+
:param cwd: Sets the current directory before the child is executed.
64+
:type cwd: typing.Optional[typing.Union[str, bytes]]
65+
:param env: Defines the environment variables for the new process.
66+
:type env: typing.Optional[typing.Mapping[typing.Union[str, bytes], typing.Union[str, bytes]]]
6067
:rtype: SubprocessExecuteAsyncResult
6168
:raises OSError: impossible to process STDIN
6269

6370
.. versionadded:: 1.2.0
6471
.. versionchanged:: 2.1.0 Use typed NamedTuple as result
72+
.. versionchanged:: 3.2.0 Expose cwd and env as optional keyword-only arguments
6573

6674
.. py:method:: execute(command, verbose=False, timeout=1*60*60, **kwargs)
6775
@@ -82,7 +90,7 @@ API: Subprocess
8290
.. versionchanged:: 1.2.0 default timeout 1 hour
8391
.. versionchanged:: 1.2.0 stdin data
8492

85-
.. py:method:: check_call(command, verbose=False, timeout=1*60*60, error_info=None, expected=None, raise_on_err=True, **kwargs)
93+
.. py:method:: check_call(command, verbose=False, timeout=1*60*60, error_info=None, expected=None, raise_on_err=True, *, exception_class=CalledProcessError, **kwargs)
8694
8795
Execute command and check for return code.
8896

@@ -98,14 +106,17 @@ API: Subprocess
98106
:type expected: ``typing.Optional[typing.Iterable[int]]``
99107
:param raise_on_err: Raise exception on unexpected return code
100108
:type raise_on_err: ``bool``
109+
:param exception_class: Exception class for errors. Subclass of CalledProcessError is mandatory.
110+
:type exception_class: typing.Type[CalledProcessError]
101111
:rtype: ExecResult
102112
:raises ExecHelperTimeoutError: Timeout exceeded
103113
:raises CalledProcessError: Unexpected exit code
104114

105115
.. versionchanged:: 1.1.0 make method
106116
.. versionchanged:: 1.2.0 default timeout 1 hour
117+
.. versionchanged:: 3.2.0 Exception class can be substituted
107118

108-
.. py:method:: check_stderr(command, verbose=False, timeout=1*60*60, error_info=None, raise_on_err=True, **kwargs)
119+
.. py:method:: check_stderr(command, verbose=False, timeout=1*60*60, error_info=None, raise_on_err=True, *, expected=None, exception_class=CalledProcessError, **kwargs)
109120
110121
Execute command expecting return code 0 and empty STDERR.
111122

@@ -119,14 +130,17 @@ API: Subprocess
119130
:type error_info: ``typing.Optional[str]``
120131
:param raise_on_err: Raise exception on unexpected return code
121132
:type raise_on_err: ``bool``
133+
:param expected: expected return codes (0 by default)
134+
:type expected: typing.Optional[typing.Iterable[typing.Union[int, ExitCodes]]]
135+
:param exception_class: Exception class for errors. Subclass of CalledProcessError is mandatory.
136+
:type exception_class: typing.Type[CalledProcessError]
122137
:rtype: ExecResult
123138
:raises ExecHelperTimeoutError: Timeout exceeded
124139
:raises CalledProcessError: Unexpected exit code or stderr presents
125140

126-
.. note:: expected return codes can be overridden via kwargs.
127-
128141
.. versionchanged:: 1.1.0 make method
129142
.. versionchanged:: 1.2.0 default timeout 1 hour
143+
.. versionchanged:: 3.2.0 Exception class can be substituted
130144

131145

132146
.. py:class:: SubprocessExecuteAsyncResult

doc/source/exceptions.rst

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,14 @@ API: exceptions
100100
``str``
101101
stdout string or brief string
102102

103-
.. py:exception:: ParallelCallExceptions(ExecCalledProcessError)
103+
.. py:exception:: ParallelCallProcessError(ExecCalledProcessError)
104104
105-
Exception raised during parallel call as result of exceptions.
105+
Exception during parallel execution.
106106

107-
.. py:method:: __init__(command, exceptions, errors, results, expected=None, )
107+
.. py:method:: __init__(command, errors, results, expected=None, )
108108
109109
:param command: command
110110
:type command: ``str``
111-
:param exceptions: Exception on connections
112-
:type exceptions: ``typing.Dict[typing.Tuple[str, int], Exception]``
113111
:param errors: results with errors
114112
:type errors: typing.Dict[typing.Tuple[str, int], ExecResult]
115113
:param results: all results
@@ -124,11 +122,6 @@ API: exceptions
124122
``str``
125123
command
126124

127-
.. py:attribute:: exceptions
128-
129-
``typing.Dict[typing.Tuple[str, int], Exception]``
130-
Exception on connections
131-
132125
.. py:attribute:: errors
133126
134127
results with errors
@@ -147,14 +140,16 @@ API: exceptions
147140

148141
:rtype: typing.List[typing.Union[int, ExitCodes]]
149142

150-
.. py:exception:: ParallelCallProcessError(ExecCalledProcessError)
143+
.. py:exception:: ParallelCallExceptions(ParallelCallProcessError)
151144
152-
Exception during parallel execution.
145+
Exception raised during parallel call as result of exceptions.
153146

154-
.. py:method:: __init__(command, errors, results, expected=None, )
147+
.. py:method:: __init__(command, exceptions, errors, results, expected=None, )
155148
156149
:param command: command
157150
:type command: ``str``
151+
:param exceptions: Exception on connections
152+
:type exceptions: ``typing.Dict[typing.Tuple[str, int], Exception]``
158153
:param errors: results with errors
159154
:type errors: typing.Dict[typing.Tuple[str, int], ExecResult]
160155
:param results: all results
@@ -169,6 +164,11 @@ API: exceptions
169164
``str``
170165
command
171166

167+
.. py:attribute:: exceptions
168+
169+
``typing.Dict[typing.Tuple[str, int], Exception]``
170+
Exception on connections
171+
172172
.. py:attribute:: errors
173173
174174
results with errors

exec_helpers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
except pkg_resources.DistributionNotFound:
5959
# package is not installed, try to get from SCM
6060
try:
61+
# noinspection PyPackageRequirements,PyUnresolvedReferences
6162
import setuptools_scm # type: ignore
6263

6364
__version__ = setuptools_scm.get_version()

0 commit comments

Comments
 (0)