Skip to content

Commit 8cd9057

Browse files
authored
Port CI, docstrings and validations (#81)
1 parent b6b4743 commit 8cd9057

File tree

11 files changed

+175
-62
lines changed

11 files changed

+175
-62
lines changed

.travis.yml

Lines changed: 58 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,89 +3,103 @@ language: python
33
os: linux
44
install:
55
- &upgrade_python_toolset pip install --upgrade pip setuptools wheel
6-
- pip install tox
7-
- pip install coveralls
8-
script: []
6+
- pip install --upgrade pytest pytest-sugar
7+
- &install_deps pip install -r CI_REQUIREMENTS.txt
8+
- pip install --upgrade pytest-cov coveralls
9+
10+
_python:
11+
- &python27
12+
name: "Python 2.7"
13+
python: 2.7
14+
- &pypy
15+
name: "PyPy"
16+
python: pypy
17+
- &python37
18+
name: "Python 3.7"
19+
python: 3.7
20+
dist: xenial
21+
sudo: true
22+
23+
_helpers:
24+
- &build_package python setup.py bdist_wheel
25+
26+
- &static_analysis
27+
stage: Static analysis
28+
<<: *python27
29+
after_success: skip
30+
31+
- &code_style_check
32+
stage: Code style check
33+
<<: *python27
34+
after_success: skip
35+
36+
script:
37+
- pip install -e .
38+
- py.test -vv --cov-config .coveragerc --cov-report= --cov=exec_helpers test
39+
- coverage report -m --fail-under 87
940
after_success:
1041
- coveralls
1142

1243
jobs:
1344
fast_finish: true
1445
include:
1546
- stage: test
16-
name: "Python 2.7"
17-
python: 2.7
18-
script:
19-
- tox -e py27
47+
<<: *python27
2048
- stage: test
21-
name: "PyPy"
22-
python: pypy
23-
script:
24-
- tox -e pypy
49+
<<: *pypy
2550

26-
- stage: Static analisys
51+
- <<: *static_analysis
2752
name: "PyLint"
28-
python: 2.7
29-
services: []
3053
install:
3154
- *upgrade_python_toolset
32-
- pip install tox
55+
- *install_deps
56+
- pip install --upgrade "pylint < 2.0"
3357
script:
34-
- tox -e pylint
35-
after_success: skip
36-
- stage: Static analisys
58+
- pylint exec_helpers
59+
- <<: *static_analysis
3760
name: "Bandit"
38-
python: 2.7
39-
services: []
4061
install:
4162
- *upgrade_python_toolset
42-
- pip install tox
63+
- pip install --upgrade bandit
4364
script:
44-
- tox -e bandit
45-
after_success: skip
46-
- stage: Static analisys
65+
- bandit -r exec_helpers
66+
- <<: *static_analysis
67+
<<: *python37
4768
name: "MyPy"
48-
python: 3.7
49-
dist: xenial
50-
sudo: true
51-
services: []
5269
install:
5370
- *upgrade_python_toolset
54-
- pip install tox
71+
- *install_deps
72+
- pip install --upgrade "mypy >= 0.620"
5573
script:
56-
- tox -e mypy
57-
after_success: skip
74+
- mypy --strict exec_helpers
5875

59-
- stage: Code style check
76+
- <<: *code_style_check
6077
name: "PEP8"
61-
python: 2.7
6278
install:
6379
- *upgrade_python_toolset
64-
- pip install tox
80+
- pip install --upgrade flake8
6581
script:
66-
- tox -e pep8
67-
after_success: skip
68-
- stage: Code style check
82+
- flake8
83+
- <<: *code_style_check
6984
name: "PEP257"
70-
python: 2.7
7185
install:
7286
- *upgrade_python_toolset
73-
- pip install tox
87+
- pip install --upgrade pydocstyle
7488
script:
75-
- tox -e pep257
76-
after_success: skip
89+
- pydocstyle exec_helpers
7790

7891
- stage: deploy
7992
# This prevents job from appearing in test plan unless commit is tagged:
8093
if: tag IS present
81-
# Run on pypy to build not cythonized wheel
82-
python: pypy
94+
<<: *pypy
95+
name: Build universal bdist_wheel. Deploy bdist and sdist.
8396
services: []
8497
install:
8598
- *upgrade_python_toolset
8699
- pip install -r build_requirements.txt
87100
script:
88-
- python setup.py bdist_wheel
101+
- *build_package
102+
before_deploy: []
89103
deploy:
90104
- provider: pypi
91105
# `skip_cleanup: true` is required to preserve binary wheels, built

build_requirements.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
1-
Cython; platform_python_implementation == "CPython"
21
wheel
32
-r CI_REQUIREMENTS.txt
4-
-r requirements.txt

exec_helpers/_ssh_client_base.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ def __call__( # type: ignore
135135
:type auth: typing.Optional[ssh_auth.SSHAuth]
136136
:param verbose: show additional error/warning messages
137137
:type verbose: bool
138+
:return: SSH client instance
138139
:rtype: SSHClientBase
139140
"""
140141
if (host, port) in cls.__cache:
@@ -178,7 +179,7 @@ def __call__( # type: ignore
178179
def clear_cache(mcs): # type: (typing.Type[_MemorizedSSH]) -> None
179180
"""Clear cached connections for initialize new instance on next call.
180181
181-
getrefcount is used to check for usage.
182+
getrefcount is used to check for usage, so connections closed on CPYTHON only.
182183
"""
183184
n_count = 4
184185
# PY3: cache, ssh, temporary
@@ -224,7 +225,9 @@ def __init__(
224225
): # type: (...) -> None
225226
"""Context manager for call commands with sudo.
226227
228+
:param ssh: connection instance
227229
:type ssh: SSHClientBase
230+
:param enforce: sudo mode for context manager
228231
:type enforce: typing.Optional[bool]
229232
"""
230233
self.__ssh = ssh
@@ -255,7 +258,9 @@ def __init__(
255258
): # type: (...) -> None
256259
"""Context manager for keepalive management.
257260
261+
:param ssh: connection instance
258262
:type ssh: SSHClientBase
263+
:param enforce: keepalive mode for context manager
259264
:type enforce: bool
260265
:param enforce: Keep connection alive after context manager exit
261266
"""
@@ -291,7 +296,7 @@ def __init__(
291296
auth=None, # type: typing.Optional[ssh_auth.SSHAuth]
292297
verbose=True, # type: bool
293298
): # type: (...) -> None
294-
"""SSHClient helper.
299+
"""Main SSH Client helper.
295300
296301
:param host: remote hostname
297302
:type host: str
@@ -393,7 +398,7 @@ def __str__(self): # type: () -> str # pragma: no cover
393398

394399
@property
395400
def _ssh(self): # type: () -> paramiko.SSHClient
396-
"""ssh client object getter for inheritance support only.
401+
"""Ssh client object getter for inheritance support only.
397402
398403
Attention: ssh client object creation and change
399404
is allowed only by __init__ and reconnect call.
@@ -534,6 +539,7 @@ def keepalive_mode(self): # type: () -> bool
534539
def keepalive_mode(self, mode): # type: (bool) -> None
535540
"""Persistent keepalive mode change for connection object.
536541
542+
:param mode: keepalive mode enable/disable
537543
:type mode: bool
538544
"""
539545
self.__keepalive_mode = bool(mode)
@@ -556,6 +562,7 @@ def sudo(
556562
557563
:param enforce: Enforce sudo enabled or disabled. By default: None
558564
:type enforce: typing.Optional[bool]
565+
:return: context manager with selected sudo state inside
559566
:rtype: typing.ContextManager
560567
"""
561568
return self.__get_sudo(ssh=self, enforce=enforce)
@@ -568,6 +575,7 @@ def keepalive(
568575
569576
:param enforce: Enforce keepalive enabled or disabled.
570577
:type enforce: bool
578+
:return: context manager with selected keepalive state inside
571579
:rtype: typing.ContextManager
572580
573581
.. Note:: Enter and exit ssh context manager is produced as well.
@@ -600,6 +608,9 @@ def execute_async(
600608
:param log_mask_re: regex lookup rule to mask command for logger.
601609
all MATCHED groups will be replaced by '<*masked*>'
602610
:type log_mask_re: typing.Optional[str]
611+
:param kwargs: additional parameters for call.
612+
:type kwargs: typing.Any
613+
:return: Tuple with control interface and file-like objects for STDIN/STDERR/STDOUT
603614
:rtype: typing.Tuple[
604615
paramiko.Channel,
605616
paramiko.ChannelFile,
@@ -669,15 +680,24 @@ def _exec_command(
669680
): # type: (...) -> exec_result.ExecResult
670681
"""Get exit status from channel with timeout.
671682
683+
:param command: executed command (for logs)
672684
:type command: str
685+
:param interface: interface to control execution
673686
:type interface: paramiko.channel.Channel
687+
:param stdout: source for STDOUT read
674688
:type stdout: typing.Optional[paramiko.ChannelFile]
689+
:param stderr: source for STDERR read
675690
:type stderr: typing.Optional[paramiko.ChannelFile]
691+
:param timeout: timeout before stop execution with TimeoutError
676692
:type timeout: typing.Union[int, float, None]
693+
:param verbose: produce log.info records for STDOUT/STDERR
677694
:type verbose: bool
678695
:param log_mask_re: regex lookup rule to mask command for logger.
679696
all MATCHED groups will be replaced by '<*masked*>'
680697
:type log_mask_re: typing.Optional[str]
698+
:param kwargs: additional parameters for call.
699+
:type kwargs: typing.Any
700+
:return: Execution result
681701
:rtype: ExecResult
682702
:raises ExecHelperTimeoutError: Timeout exceeded
683703
@@ -784,6 +804,9 @@ def execute_through_host(
784804
:type timeout: typing.Union[int, float, None]
785805
:param get_pty: open PTY on target machine
786806
:type get_pty: bool
807+
:param kwargs: additional parameters for call.
808+
:type kwargs: typing.Any
809+
:return: Execution result
787810
:rtype: ExecResult
788811
:raises ExecHelperTimeoutError: Timeout exceeded
789812
@@ -859,6 +882,8 @@ def execute_together(
859882
:type expected: typing.Optional[typing.Iterable[]]
860883
:param raise_on_err: Raise exception on unexpected return code
861884
:type raise_on_err: bool
885+
:param kwargs: additional parameters for execute_async call.
886+
:type kwargs: typing.Any
862887
:return: dictionary {(hostname, port): result}
863888
:rtype: typing.Dict[typing.Tuple[str, int], exec_result.ExecResult]
864889
:raises ParallelCallProcessError: Unexpected any code at lest on one target
@@ -946,16 +971,21 @@ def get_result(remote): # type: (SSHClientBase) -> exec_result.ExecResult
946971
def open(self, path, mode='r'): # type: (str, str) -> paramiko.SFTPFile
947972
"""Open file on remote using SFTP session.
948973
974+
:param path: filesystem object path
949975
:type path: str
976+
:param mode: open file mode ('t' is not supported)
950977
:type mode: str
951978
:return: file.open() stream
979+
:rtype: paramiko.SFTPFile
952980
"""
953981
return self._sftp.open(path, mode) # pragma: no cover
954982

955983
def exists(self, path): # type: (str) -> bool
956984
"""Check for file existence using SFTP session.
957985
986+
:param path: filesystem object path
958987
:type path: str
988+
:return: path is valid (object exists)
959989
:rtype: bool
960990
"""
961991
try:
@@ -967,7 +997,9 @@ def exists(self, path): # type: (str) -> bool
967997
def stat(self, path): # type: (str) -> paramiko.sftp_attr.SFTPAttributes
968998
"""Get stat info for path with following symlinks.
969999
1000+
:param path: filesystem object path
9701001
:type path: str
1002+
:return: stat like information for remote path
9711003
:rtype: paramiko.sftp_attr.SFTPAttributes
9721004
"""
9731005
return self._sftp.stat(path) # pragma: no cover
@@ -991,7 +1023,9 @@ def utime(
9911023
def isfile(self, path): # type: (str) -> bool
9921024
"""Check, that path is file using SFTP session.
9931025
1026+
:param path: remote path to validate
9941027
:type path: str
1028+
:return: path is file
9951029
:rtype: bool
9961030
"""
9971031
try:
@@ -1003,7 +1037,9 @@ def isfile(self, path): # type: (str) -> bool
10031037
def isdir(self, path): # type: (str) -> bool
10041038
"""Check, that path is directory using SFTP session.
10051039
1040+
:param path: remote path to validate
10061041
:type path: str
1042+
:return: path is directory
10071043
:rtype: bool
10081044
"""
10091045
try:

0 commit comments

Comments
 (0)