Skip to content

Commit 2ca7c67

Browse files
authored
Merge pull request #4 from mbdevpl/feature/more-tests
test on Jenkins
2 parents ac44ddd + 120d878 commit 2ca7c67

9 files changed

Lines changed: 270 additions & 19 deletions

File tree

.dockerignore

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
## /.gitignore
2+
3+
# IDE: Visual Studio Code
4+
/.vscode
5+
6+
# OS: macOS
7+
.DS_Store
8+
9+
# OS: Windows
10+
desktop.ini
11+
System Volume Information
12+
Thumbs.db
13+
Thumbs.db*
14+
15+
# Python
16+
/build
17+
/dist
18+
/.cache
19+
__pycache__
20+
*.egg
21+
*.egg-info
22+
*.pyc
23+
24+
# Python: coverage
25+
/htmlcov
26+
/.coverage
27+
28+
# Python: Jupyter notebooks
29+
.ipynb_checkpoints
30+
*-checkpoint.ipynb

.gitignore

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
# Jupyter Notebook
2-
/.ipynb_checkpoints
3-
*-checkpoint.ipynb
1+
# IDE: Visual Studio Code
2+
/.vscode
43

54
# OS: macOS
65
.DS_Store
76

7+
# OS: Windows
8+
desktop.ini
9+
System Volume Information
10+
Thumbs.db
11+
Thumbs.db*
12+
813
# Python
914
/build
1015
/dist
@@ -14,6 +19,10 @@ __pycache__
1419
*.egg-info
1520
*.pyc
1621

17-
# Python coverage
22+
# Python: coverage
1823
/htmlcov
1924
/.coverage
25+
26+
# Python: Jupyter notebooks
27+
.ipynb_checkpoints
28+
*-checkpoint.ipynb

Dockerfile

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
ARG PYTHON_VERSION="3.10"
2+
3+
FROM python:${PYTHON_VERSION}
4+
5+
SHELL ["/bin/bash", "-c"]
6+
7+
# set timezone
8+
9+
ARG TIMEZONE="Europe/Warsaw"
10+
11+
RUN set -Eeuxo pipefail && \
12+
apt-get update && \
13+
apt-get install --no-install-recommends -y \
14+
tzdata && \
15+
echo "${TIMEZONE}" > /etc/timezone && \
16+
cp "/usr/share/zoneinfo/${TIMEZONE}" /etc/localtime && \
17+
apt-get -qy autoremove && \
18+
apt-get clean && \
19+
rm -rf /var/lib/apt/lists/*
20+
21+
# add a non-root user
22+
23+
ARG USER_ID=1000
24+
ARG GROUP_ID=1000
25+
ARG AUX_GROUP_IDS=""
26+
27+
RUN set -Eeuxo pipefail && \
28+
addgroup --gid "${GROUP_ID}" user && \
29+
adduser --disabled-password --gecos "User" --uid "${USER_ID}" --gid "${GROUP_ID}" user && \
30+
echo ${AUX_GROUP_IDS} | xargs -n1 echo | xargs -I% addgroup --gid % group% && \
31+
echo ${AUX_GROUP_IDS} | xargs -n1 echo | xargs -I% usermod --append --groups group% user
32+
33+
# prepare argunparse for testing
34+
35+
WORKDIR /home/user/argunparse
36+
37+
COPY --chown=${USER_ID}:${GROUP_ID} requirements*.txt ./
38+
39+
RUN set -Eeuxo pipefail && \
40+
pip3 install -r requirements_ci.txt
41+
42+
USER user
43+
44+
VOLUME ["/home/user/argunparse"]

Jenkinsfile

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#!/usr/bin/env groovy
2+
3+
library 'jenkins-mbdev-pl-libs'
4+
5+
pipeline {
6+
7+
options {
8+
ansiColor('xterm')
9+
}
10+
11+
environment {
12+
PYTHON_MODULES = 'argunparse test *.py'
13+
}
14+
15+
agent any
16+
17+
stages {
18+
stage('Matrix') {
19+
matrix {
20+
21+
axes {
22+
axis {
23+
name 'PYTHON_VERSION'
24+
values '3.8', '3.9', '3.10'
25+
}
26+
}
27+
28+
agent {
29+
dockerfile {
30+
additionalBuildArgs '--build-arg USER_ID=${USER_ID} --build-arg GROUP_ID=${GROUP_ID}' \
31+
+ ' --build-arg AUX_GROUP_IDS="${AUX_GROUP_IDS}" --build-arg TIMEZONE=${TIMEZONE}' \
32+
+ ' --build-arg PYTHON_VERSION=${PYTHON_VERSION}'
33+
label 'docker'
34+
}
35+
}
36+
37+
stages {
38+
39+
stage('Lint') {
40+
when {
41+
environment name: 'PYTHON_VERSION', value: '3.10'
42+
}
43+
steps {
44+
sh """#!/usr/bin/env bash
45+
set -Eeux
46+
python -m pylint ${PYTHON_MODULES} |& tee pylint.log
47+
echo "\${PIPESTATUS[0]}" | tee pylint_status.log
48+
python -m mypy ${PYTHON_MODULES} |& tee mypy.log
49+
echo "\${PIPESTATUS[0]}" | tee mypy_status.log
50+
python -m flake518 ${PYTHON_MODULES} |& tee flake518.log
51+
echo "\${PIPESTATUS[0]}" | tee flake518_status.log
52+
python -m pydocstyle ${PYTHON_MODULES} |& tee pydocstyle.log
53+
echo "\${PIPESTATUS[0]}" | tee pydocstyle_status.log
54+
"""
55+
}
56+
}
57+
58+
stage('Test') {
59+
steps {
60+
sh '''#!/usr/bin/env bash
61+
set -Eeuxo pipefail
62+
TEST_PACKAGING=1 python -m coverage run --branch --source . -m unittest -v
63+
'''
64+
}
65+
}
66+
67+
stage('Coverage') {
68+
when {
69+
environment name: 'PYTHON_VERSION', value: '3.10'
70+
}
71+
steps {
72+
sh '''#!/usr/bin/env bash
73+
set -Eeux
74+
python -m coverage report --show-missing |& tee coverage.log
75+
echo "${PIPESTATUS[0]}" | tee coverage_status.log
76+
'''
77+
script {
78+
defaultHandlers.afterPythonBuild()
79+
}
80+
}
81+
}
82+
83+
stage('Codecov') {
84+
environment {
85+
CODECOV_TOKEN = credentials('codecov-token-mbdevpl-argunparse')
86+
}
87+
steps {
88+
sh '''#!/usr/bin/env bash
89+
set -Eeuxo pipefail
90+
python -m codecov --token ${CODECOV_TOKEN}
91+
'''
92+
}
93+
}
94+
95+
}
96+
97+
}
98+
}
99+
}
100+
101+
post {
102+
unsuccessful {
103+
script {
104+
defaultHandlers.afterBuildFailed()
105+
}
106+
}
107+
regression {
108+
script {
109+
defaultHandlers.afterBuildBroken()
110+
}
111+
}
112+
fixed {
113+
script {
114+
defaultHandlers.afterBuildFixed()
115+
}
116+
}
117+
}
118+
119+
}

README.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ Reversed argparse: generate string of command-line args from Python objects.
1515
:target: https://pypi.org/project/argunparse
1616
:alt: package version from PyPI
1717

18+
.. image:: https://github.com/mbdevpl/argunparse/actions/workflows/python.yml/badge.svg?branch=main
19+
:target: https://github.com/mbdevpl/argunparse/actions
20+
:alt: build status from GitHub
21+
1822
.. image:: https://codecov.io/gh/mbdevpl/argunparse/branch/main/graph/badge.svg
1923
:target: https://codecov.io/gh/mbdevpl/argunparse
2024
:alt: test coverage from Codecov
@@ -30,6 +34,9 @@ Reversed argparse: generate string of command-line args from Python objects.
3034
The *argunparse* is intended to perform an approximate reverse of what *argparse* does. In short:
3135
generating string (or a list of strings) of command-line arguments from a dict and/or a list.
3236

37+
.. contents::
38+
:backlinks: none
39+
3340

3441
How to use
3542
==========

argunparse/argument_unparser.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,13 @@ def unparse_arg(self, arg: t.Any) -> str: # pylint: disable = no-self-use
4949
def unparse_args(self, arguments: t.Sequence[t.Any],
5050
*, to_list: bool = False) -> t.Union[str, t.List[str]]:
5151
"""Convert list to string of command-line args."""
52-
unparsed = []
52+
unparsed_list = []
5353
for arg in arguments:
54-
unparsed.append(self.unparse_arg(arg))
55-
_LOG.debug('%s: unparsed args to %s', self, unparsed)
56-
if not to_list:
57-
unparsed = ' '.join(unparsed)
54+
unparsed_list.append(self.unparse_arg(arg))
55+
_LOG.debug('%s: unparsed args to %s', self, unparsed_list)
56+
if to_list:
57+
return unparsed_list
58+
unparsed = ' '.join(unparsed_list)
5859
_LOG.debug('%s: converted unparsed args to string "%s"', self, unparsed)
5960
return unparsed
6061

@@ -63,15 +64,15 @@ def unparse_option(self, key: str, value: t.Any,
6364
"""Convert a key-value pair into a string that can be used as a command-line option."""
6465
if option_should_be_skipped(value):
6566
return [] if to_list else ''
66-
unparsed_key = '{}{}'.format(self._long_opt if len(key) > 1 else self._short_opt, key)
67+
unparsed_key = f'{self._long_opt if len(key) > 1 else self._short_opt}{key}'
6768
if not treat_as_option_with_no_value(value):
6869
unparsed_value = self.unparse_arg(value)
6970
if to_list and (self._opt_value == ' ' or treat_as_option_with_no_value(value)):
7071
if treat_as_option_with_no_value(value):
7172
return [unparsed_key]
7273
return [unparsed_key, unparsed_value]
7374
if not treat_as_option_with_no_value(value):
74-
unparsed_option = '{}{}{}'.format(unparsed_key, self._opt_value, unparsed_value)
75+
unparsed_option = f'{unparsed_key}{self._opt_value}{unparsed_value}'
7576
if to_list:
7677
return [unparsed_option]
7778
if treat_as_option_with_no_value(value):
@@ -81,18 +82,20 @@ def unparse_option(self, key: str, value: t.Any,
8182
def unparse_options(self, options: t.Mapping[str, t.Any],
8283
*, to_list: bool = False) -> t.Union[str, t.List[str]]:
8384
"""Convert dictionary to string of command-line args."""
84-
unparsed = []
85+
unparsed_list: t.List[str] = []
8586
for key, value in options.items():
8687
if option_should_be_skipped(value):
8788
continue
8889
unparsed_option = self.unparse_option(key, value, to_list=to_list)
8990
if to_list:
90-
unparsed += unparsed_option
91+
unparsed_list += unparsed_option
9192
else:
92-
unparsed.append(unparsed_option)
93-
_LOG.debug('%s: unparsed options to %s', self, unparsed)
94-
if not to_list:
95-
unparsed = ' '.join(unparsed)
93+
assert isinstance(unparsed_option, str), type(unparsed_option)
94+
unparsed_list.append(unparsed_option)
95+
_LOG.debug('%s: unparsed options to %s', self, unparsed_list)
96+
if to_list:
97+
return unparsed_list
98+
unparsed = ' '.join(unparsed_list)
9699
_LOG.debug('%s: converted unparsed options to string "%s"', self, unparsed)
97100
return unparsed
98101

pyproject.toml

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,35 @@
11
[build-system]
2-
requires=['docutils', 'setuptools', 'version-query', 'wheel']
2+
requires = ['docutils', 'setuptools', 'version-query', 'wheel']
3+
4+
[tool.flake8]
5+
max-line-length = 100
6+
max-doc-length = 100
7+
8+
[tool.pydocstyle]
9+
ignore = [
10+
'D102', 'D103', 'D105', 'D107',
11+
'D203', 'D213',
12+
'D406', 'D407', 'D412', 'D413'
13+
]
14+
15+
[tool.pylint.MASTER]
16+
load-plugins = [
17+
'pylint.extensions.broad_try_clause',
18+
'pylint.extensions.mccabe',
19+
'pylint.extensions.no_self_use',
20+
'pylint.extensions.redefined_variable_type'
21+
]
22+
23+
[tool.pylint.'MESSAGES CONTROL']
24+
docstring-min-length = 5
25+
26+
[tool.pylint.SIMILARITIES]
27+
ignore-imports = 'yes'
28+
min-similarity-lines = 5
29+
30+
[tool.pylint.BASIC]
31+
no-docstring-rgx = '^(test)?_|.*Tests$'
32+
unsafe-load-any-extension = 'yes'
33+
34+
[tool.pylint.REPORTS]
35+
output-format = 'colorized'

requirements_ci.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
codecov ~= 2.1
22
coverage ~= 6.2
3+
flake518 ~= 1.2
4+
mypy ~= 0.930
5+
pydocstyle ~= 6.1
6+
pylint ~= 2.12
7+
types-docutils ~= 0.17
8+
types-setuptools ~= 57.4
39
-r requirements_test.txt

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class Package(setup_boilerplate.Package):
77
"""Package metadata."""
88

99
name = 'argunparse'
10-
description = 'argunparse is intended to perform approximate reverse of argparse'
10+
description = 'Reversed argparse: generate string of command-line args from Python objects.'
1111
url = 'https://github.com/mbdevpl/argunparse'
1212
classifiers = [
1313
'Development Status :: 4 - Beta',

0 commit comments

Comments
 (0)