From: garciadeblas Date: Wed, 10 May 2023 15:25:47 +0000 (+0200) Subject: Support of Python3.10 X-Git-Tag: release-v14.0-start~28 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=refs%2Fchanges%2F73%2F13373%2F8;p=osm%2Ftests.git Support of Python3.10 This change includes required changes to make tests work in Python3.10. It also enables tox running as part of stage-test, which was not included before. For the moment, only flake8 and black envs will be checked. Some pylint errors have been fixed, but a few remain unsolved. That's why pylint is run, but its result is ignored. Change-Id: I29105bf849eab8b47f92627a7eea1ede31b8e05b Signed-off-by: garciadeblas --- diff --git a/Dockerfile b/Dockerfile index 6411021..9206d0c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,7 +22,7 @@ # devops-stages/stage-build.sh # -FROM ubuntu:20.04 +FROM ubuntu:22.04 ARG APT_PROXY RUN if [ ! -z $APT_PROXY ] ; then \ @@ -33,5 +33,20 @@ RUN if [ ! -z $APT_PROXY ] ; then \ RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get -y install \ debhelper \ - dh-make \ - git + dh-python \ + git \ + python3 \ + python3-all \ + python3-dev \ + python3-setuptools \ + python3-pip \ + tox + +RUN DEBIAN_FRONTEND=noninteractive apt-get -y install \ + libcurl4-openssl-dev \ + libssl-dev \ + dh-make + +ENV LC_ALL C.UTF-8 +ENV LANG C.UTF-8 + diff --git a/debian/control b/debian/control index daa9f1a..1aa80b5 100644 --- a/debian/control +++ b/debian/control @@ -18,7 +18,7 @@ Source: osm-tests Section: devel Priority: optional Maintainer: Gerardo Garcia -Build-Depends: debhelper (>=9) +Build-Depends: debhelper (>=9), debhelper-compat (=13) Standards-Version: 3.9.6 Homepage: http://osm.etsi.org diff --git a/devops-stages/stage-test.sh b/devops-stages/stage-test.sh index 31161ac..335f9de 100755 --- a/devops-stages/stage-test.sh +++ b/devops-stages/stage-test.sh @@ -68,6 +68,8 @@ check_spaces_eol () { exit 1 } +echo "Checking syntax of Robot tests" + echo "Checking tabs in robot files. No tabs should be present" check_tabs robot-systest/testsuite check_tabs robot-systest/lib @@ -100,6 +102,6 @@ echo "No presence of spaces at EOL in robot files. Correct!" # - Settings: 1 blank line to separate each kind of setting (all LIbrary together, all Resource together, etc.) # - Test cases: 2 blank lines between test cases, max 1 blank line inside Test case, no blank line after Test Case Keyword -echo "SUCCESS" -exit 0 +echo "Launching tox" +TOX_PARALLEL_NO_SPINNER=1 tox --parallel=auto diff --git a/requirements-dev.in b/requirements-dev.in index dc09a5e..b6a02f4 100644 --- a/requirements-dev.in +++ b/requirements-dev.in @@ -13,8 +13,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -git+https://osm.etsi.org/gerrit/osm/IM.git@master#egg=osm-im --r https://osm.etsi.org/gitweb/?p=osm/IM.git;a=blob_plain;f=requirements.txt;hb=master - -git+https://osm.etsi.org/gerrit/osm/osmclient.git@master#egg=osm-osmclient +git+https://osm.etsi.org/gerrit/osm/osmclient.git@master#egg=osmclient -r https://osm.etsi.org/gitweb/?p=osm/osmclient.git;a=blob_plain;f=requirements.txt;hb=master diff --git a/requirements-dev.txt b/requirements-dev.txt index ef3915f..79195cd 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -14,72 +14,41 @@ # See the License for the specific language governing permissions and # limitations under the License. ####################################################################################### -bitarray==2.6.2 - # via - # -r https://osm.etsi.org/gitweb/?p=osm/IM.git;a=blob_plain;f=requirements.txt;hb=master - # pyangbind -certifi==2022.12.7 +certifi==2023.5.7 # via # -r https://osm.etsi.org/gitweb/?p=osm/osmclient.git;a=blob_plain;f=requirements.txt;hb=master # requests -charset-normalizer==3.0.1 +charset-normalizer==3.1.0 # via # -r https://osm.etsi.org/gitweb/?p=osm/osmclient.git;a=blob_plain;f=requirements.txt;hb=master # requests click==8.1.3 # via -r https://osm.etsi.org/gitweb/?p=osm/osmclient.git;a=blob_plain;f=requirements.txt;hb=master -enum34==1.1.10 - # via - # -r https://osm.etsi.org/gitweb/?p=osm/IM.git;a=blob_plain;f=requirements.txt;hb=master - # pyangbind idna==3.4 # via # -r https://osm.etsi.org/gitweb/?p=osm/osmclient.git;a=blob_plain;f=requirements.txt;hb=master # requests jinja2==3.1.2 # via -r https://osm.etsi.org/gitweb/?p=osm/osmclient.git;a=blob_plain;f=requirements.txt;hb=master -lxml==4.9.2 - # via - # -r https://osm.etsi.org/gitweb/?p=osm/IM.git;a=blob_plain;f=requirements.txt;hb=master - # pyang - # pyangbind markupsafe==2.1.2 # via # -r https://osm.etsi.org/gitweb/?p=osm/osmclient.git;a=blob_plain;f=requirements.txt;hb=master # jinja2 -osm-im @ git+https://osm.etsi.org/gerrit/osm/IM.git@master - # via -r requirements-dev.in osmclient @ git+https://osm.etsi.org/gerrit/osm/osmclient.git@master # via -r requirements-dev.in -packaging==23.0 +packaging==23.1 # via -r https://osm.etsi.org/gitweb/?p=osm/osmclient.git;a=blob_plain;f=requirements.txt;hb=master -prettytable==3.6.0 +prettytable==3.7.0 # via -r https://osm.etsi.org/gitweb/?p=osm/osmclient.git;a=blob_plain;f=requirements.txt;hb=master -pyang==2.5.3 - # via - # -r https://osm.etsi.org/gitweb/?p=osm/IM.git;a=blob_plain;f=requirements.txt;hb=master - # pyangbind -pyangbind==0.8.1 - # via -r https://osm.etsi.org/gitweb/?p=osm/IM.git;a=blob_plain;f=requirements.txt;hb=master pycurl==7.45.2 # via -r https://osm.etsi.org/gitweb/?p=osm/osmclient.git;a=blob_plain;f=requirements.txt;hb=master python-magic==0.4.27 # via -r https://osm.etsi.org/gitweb/?p=osm/osmclient.git;a=blob_plain;f=requirements.txt;hb=master pyyaml==5.4.1 - # via - # -r https://osm.etsi.org/gitweb/?p=osm/IM.git;a=blob_plain;f=requirements.txt;hb=master - # -r https://osm.etsi.org/gitweb/?p=osm/osmclient.git;a=blob_plain;f=requirements.txt;hb=master -regex==2022.10.31 - # via - # -r https://osm.etsi.org/gitweb/?p=osm/IM.git;a=blob_plain;f=requirements.txt;hb=master - # pyangbind -requests==2.28.2 # via -r https://osm.etsi.org/gitweb/?p=osm/osmclient.git;a=blob_plain;f=requirements.txt;hb=master -six==1.16.0 - # via - # -r https://osm.etsi.org/gitweb/?p=osm/IM.git;a=blob_plain;f=requirements.txt;hb=master - # pyangbind -urllib3==1.26.14 +requests==2.30.0 + # via -r https://osm.etsi.org/gitweb/?p=osm/osmclient.git;a=blob_plain;f=requirements.txt;hb=master +urllib3==2.0.2 # via # -r https://osm.etsi.org/gitweb/?p=osm/osmclient.git;a=blob_plain;f=requirements.txt;hb=master # requests diff --git a/requirements.txt b/requirements.txt index 9a55288..0e1f6c1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,13 +16,11 @@ ####################################################################################### appdirs==1.4.4 # via openstacksdk -argcomplete==2.0.0 +argcomplete==3.0.8 # via yq async-generator==1.10 - # via - # trio - # trio-websocket -attrs==22.2.0 + # via trio +attrs==23.1.0 # via # cmd2 # jsonschema @@ -32,13 +30,13 @@ autopage==0.5.1 # via cliff bcrypt==4.0.1 # via paramiko -bitarray==2.6.2 +bitarray==2.7.3 # via # -r requirements.in # pyangbind blessings==1.7 # via charm-tools -certifi==2022.12.7 +certifi==2023.5.7 # via # requests # selenium @@ -46,13 +44,13 @@ cffi==1.15.1 # via # cryptography # pynacl -charm-tools==3.0.4 +charm-tools==3.0.6 # via -r requirements.in -charset-normalizer==3.0.1 +charset-normalizer==3.1.0 # via requests cheetah3==3.2.6.post1 # via charm-tools -cliff==4.1.0 +cliff==4.3.0 # via # osc-lib # python-openstackclient @@ -60,7 +58,7 @@ cmd2==2.4.3 # via cliff colander==1.8.3 # via charm-tools -cryptography==39.0.0 +cryptography==40.0.2 # via # openstacksdk # paramiko @@ -80,13 +78,15 @@ dict2colander==0.2 # via charm-tools distlib==0.3.6 # via virtualenv -dogpile-cache==1.1.8 +dogpile-cache==1.2.0 # via openstacksdk enum34==1.1.10 # via pyangbind -exceptiongroup==1.1.0 - # via trio -filelock==3.9.0 +exceptiongroup==1.1.1 + # via + # trio + # trio-websocket +filelock==3.12.0 # via virtualenv h11==0.14.0 # via wsproto @@ -96,14 +96,10 @@ idna==3.4 # via # requests # trio -importlib-metadata==6.0.0 +importlib-metadata==6.6.0 # via # cliff # keyring -importlib-resources==5.10.2 - # via - # jsonschema - # keyring iso8601==1.1.0 # via # colander @@ -139,7 +135,7 @@ jujubundlelib==0.5.7 # via charm-tools keyring==23.13.1 # via charm-tools -keystoneauth1==5.1.1 +keystoneauth1==5.1.2 # via # openstacksdk # osc-lib @@ -150,12 +146,10 @@ lxml==4.9.2 # via # pyang # pyangbind -more-itertools==9.0.0 +more-itertools==9.1.0 # via jaraco-classes -msgpack==1.0.4 +msgpack==1.0.5 # via oslo-serialization -munch==2.5.0 - # via openstacksdk netaddr==0.8.0 # via # oslo-config @@ -166,7 +160,7 @@ netifaces==0.11.0 # oslo-utils objectpath==0.6.1 # via robotframework-jsonvalidator -openstacksdk==0.103.0 +openstacksdk==1.1.0 # via # osc-lib # python-openstackclient @@ -174,11 +168,11 @@ os-service-types==1.7.0 # via # keystoneauth1 # openstacksdk -osc-lib==2.6.2 +osc-lib==2.8.0 # via python-openstackclient -oslo-config==9.1.0 +oslo-config==9.1.1 # via python-keystoneclient -oslo-i18n==5.1.0 +oslo-i18n==6.0.0 # via # osc-lib # oslo-config @@ -187,7 +181,7 @@ oslo-i18n==5.1.0 # python-keystoneclient # python-novaclient # python-openstackclient -oslo-serialization==5.0.0 +oslo-serialization==5.1.1 # via # python-keystoneclient # python-novaclient @@ -203,11 +197,11 @@ otherstuf==1.1.0 # via charm-tools outcome==1.2.0 # via trio -packaging==23.0 +packaging==23.1 # via # oslo-utils # python-keystoneclient -paramiko==3.0.0 +paramiko==3.1.0 # via # robotframework-sshlibrary # scp @@ -233,15 +227,13 @@ pbr==5.11.1 # python-novaclient # python-openstackclient # stevedore -pkgutil-resolve-name==1.3.10 - # via jsonschema -platformdirs==2.6.2 +platformdirs==3.5.0 # via virtualenv ply==3.11 # via # jsonpath-ng # jsonpath-rw -prettytable==3.6.0 +prettytable==3.7.0 # via # cliff # python-cinderclient @@ -264,15 +256,15 @@ pyrsistent==0.19.3 # via jsonschema pysocks==1.7.1 # via urllib3 -python-cinderclient==9.2.0 +python-cinderclient==9.3.0 # via python-openstackclient -python-keystoneclient==5.0.1 +python-keystoneclient==5.1.0 # via python-openstackclient -python-novaclient==18.2.0 +python-novaclient==18.3.0 # via python-openstackclient -python-openstackclient==6.0.0 +python-openstackclient==6.2.0 # via -r requirements.in -pytz==2022.7.1 +pytz==2023.3 # via # oslo-serialization # oslo-utils @@ -286,11 +278,11 @@ pyyaml==5.4.1 # oslo-config # robotframework-yamllibrary # yq -regex==2022.10.31 +regex==2023.5.5 # via # -r requirements.in # pyangbind -requests==2.28.2 +requests==2.30.0 # via # -r requirements.in # charm-tools @@ -317,46 +309,43 @@ robotframework-jsonlibrary==0.5 # via -r requirements.in robotframework-jsonvalidator==2.0.0 # via -r requirements.in -robotframework-pythonlibcore==4.0.0 +robotframework-pythonlibcore==4.1.2 # via robotframework-seleniumlibrary robotframework-requests==0.9.4 # via -r requirements.in -robotframework-seleniumlibrary==6.0.0 +robotframework-seleniumlibrary==6.1.0 # via -r requirements.in robotframework-sshlibrary==3.8.0 # via -r requirements.in robotframework-yamllibrary==0.2.8 # via -r requirements.in -ruamel-yaml==0.17.21 +ruamel-yaml==0.17.26 # via charm-tools ruamel-yaml-clib==0.2.7 # via ruamel-yaml -scp==0.14.4 +scp==0.14.5 # via robotframework-sshlibrary secretstorage==3.3.3 # via # charm-tools # keyring -selenium==4.8.0 +selenium==4.9.1 # via robotframework-seleniumlibrary -simplejson==3.18.1 - # via - # osc-lib - # python-cinderclient +simplejson==3.19.1 + # via osc-lib six==1.16.0 # via # blessings # jsonpath-ng # jsonpath-rw # keystoneauth1 - # munch # pyangbind # python-keystoneclient sniffio==1.3.0 # via trio sortedcontainers==2.4.0 # via trio -stevedore==4.1.1 +stevedore==5.0.0 # via # cliff # dogpile-cache @@ -369,7 +358,7 @@ stevedore==4.1.1 # python-openstackclient stuf==0.9.16 # via otherstuf -toml==0.10.2 +tomlkit==0.11.8 # via yq translationstring==1.4 # via colander @@ -377,13 +366,11 @@ trio==0.22.0 # via # selenium # trio-websocket -trio-websocket==0.9.2 +trio-websocket==0.10.2 # via selenium -types-docutils==0.19.1.2 - # via types-setuptools -types-setuptools==65.7.0.3 +types-setuptools==67.7.0.2 # via requirements-parser -urllib3[socks]==1.26.14 +urllib3[socks]==2.0.2 # via # requests # selenium @@ -391,24 +378,22 @@ verboselogs==1.7 # via -r requirements.in vergit==1.0.2 # via charm-tools -virtualenv==20.17.1 +virtualenv==20.23.0 # via charm-tools wcwidth==0.2.6 # via # cmd2 # prettytable -wrapt==1.14.1 +wrapt==1.15.0 # via debtcollector wsproto==1.2.0 # via trio-websocket xmltodict==0.13.0 # via yq -yq==3.1.0 +yq==3.2.2 # via -r requirements.in -zipp==3.12.0 - # via - # importlib-metadata - # importlib-resources +zipp==3.15.0 + # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: # pip diff --git a/robot-systest/lib/renderTemplate.py b/robot-systest/lib/renderTemplate.py index d472eaa..2c7bb39 100644 --- a/robot-systest/lib/renderTemplate.py +++ b/robot-systest/lib/renderTemplate.py @@ -14,8 +14,8 @@ from jinja2 import Template -class renderTemplate(): +class renderTemplate: def render_template(self, template_file, config_file, **kwargs): """Renders a template with the values provided diff --git a/robot-systest/resources/__init__.py b/robot-systest/resources/__init__.py index d10c534..04f7d49 100644 --- a/robot-systest/resources/__init__.py +++ b/robot-systest/resources/__init__.py @@ -14,4 +14,3 @@ # See the License for the specific language governing permissions and # limitations under the License. ####################################################################################### - diff --git a/robot-systest/resources/basic_01-crud_operations_on_vim_targets_data.py b/robot-systest/resources/basic_01-crud_operations_on_vim_targets_data.py index c56d51a..101f3d5 100644 --- a/robot-systest/resources/basic_01-crud_operations_on_vim_targets_data.py +++ b/robot-systest/resources/basic_01-crud_operations_on_vim_targets_data.py @@ -45,19 +45,19 @@ if cloud_type == "openstack": raise Exception("Openstack clouds file not found") with clouds_file_path.open() as clouds_file: clouds = yaml.safe_load(clouds_file) - if not os_cloud in clouds["clouds"]: + if os_cloud not in clouds["clouds"]: raise Exception("Openstack cloud '" + os_cloud + "' not found") cloud = clouds["clouds"][os_cloud] - if not "username" in cloud["auth"]: + if "username" not in cloud["auth"]: raise Exception("Username not found in Openstack cloud '" + os_cloud + "'") vim_user = cloud["auth"]["username"] - if not "password" in cloud["auth"]: + if "password" not in cloud["auth"]: raise Exception("Password not found in Openstack cloud '" + os_cloud + "'") vim_password = cloud["auth"]["password"] - if not "auth_url" in cloud["auth"]: + if "auth_url" not in cloud["auth"]: raise Exception("Auth url not found in Openstack cloud '" + os_cloud + "'") vim_auth_url = cloud["auth"]["auth_url"] - if not "project_name" in cloud["auth"]: + if "project_name" not in cloud["auth"]: raise Exception( "Project name not found in Openstack cloud '" + os_cloud + "'" ) diff --git a/robot-systest/resources/basic_08-disable_port_security_network_level_data.py b/robot-systest/resources/basic_08-disable_port_security_network_level_data.py index 62cb9c3..b35ed87 100644 --- a/robot-systest/resources/basic_08-disable_port_security_network_level_data.py +++ b/robot-systest/resources/basic_08-disable_port_security_network_level_data.py @@ -44,19 +44,19 @@ if not clouds_file_path.exists(): raise Exception("Openstack clouds file not found") with clouds_file_path.open() as clouds_file: clouds = yaml.safe_load(clouds_file) - if not os_cloud in clouds["clouds"]: + if os_cloud not in clouds["clouds"]: raise Exception("Openstack cloud '" + os_cloud + "' not found") cloud = clouds["clouds"][os_cloud] - if not "username" in cloud["auth"]: + if "username" not in cloud["auth"]: raise Exception("Username not found in Openstack cloud '" + os_cloud + "'") vim_user = cloud["auth"]["username"] - if not "password" in cloud["auth"]: + if "password" not in cloud["auth"]: raise Exception("Password not found in Openstack cloud '" + os_cloud + "'") vim_password = cloud["auth"]["password"] - if not "auth_url" in cloud["auth"]: + if "auth_url" not in cloud["auth"]: raise Exception("Auth url not found in Openstack cloud '" + os_cloud + "'") vim_auth_url = cloud["auth"]["auth_url"] - if not "project_name" in cloud["auth"]: + if "project_name" not in cloud["auth"]: raise Exception("Project name not found in Openstack cloud '" + os_cloud + "'") vim_tenant = cloud["auth"]["project_name"] vim_user_domain_name = ( diff --git a/robot-systest/resources/epa_03-crud_operations_on_sdnc_data.py b/robot-systest/resources/epa_03-crud_operations_on_sdnc_data.py index 3267dd2..c11054a 100644 --- a/robot-systest/resources/epa_03-crud_operations_on_sdnc_data.py +++ b/robot-systest/resources/epa_03-crud_operations_on_sdnc_data.py @@ -11,9 +11,6 @@ # limitations under the License. import os -from pathlib import Path - -import yaml from common_helpers import get_prometheus_info diff --git a/robot-systest/resources/sa_01-vnf_with_vim_metrics_data.py b/robot-systest/resources/sa_01-vnf_with_vim_metrics_data.py index 27f448f..a7a3456 100644 --- a/robot-systest/resources/sa_01-vnf_with_vim_metrics_data.py +++ b/robot-systest/resources/sa_01-vnf_with_vim_metrics_data.py @@ -10,8 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path - from common_helpers import get_prometheus_info diff --git a/robot-systest/resources/sa_02-vnf_with_vim_metrics_and_autoscaling_data.py b/robot-systest/resources/sa_02-vnf_with_vim_metrics_and_autoscaling_data.py index 27f448f..a7a3456 100644 --- a/robot-systest/resources/sa_02-vnf_with_vim_metrics_and_autoscaling_data.py +++ b/robot-systest/resources/sa_02-vnf_with_vim_metrics_and_autoscaling_data.py @@ -10,8 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path - from common_helpers import get_prometheus_info diff --git a/robot-systest/resources/sa_07-alarms_from_sa-related_vnfs_data.py b/robot-systest/resources/sa_07-alarms_from_sa-related_vnfs_data.py index 27f448f..a7a3456 100644 --- a/robot-systest/resources/sa_07-alarms_from_sa-related_vnfs_data.py +++ b/robot-systest/resources/sa_07-alarms_from_sa-related_vnfs_data.py @@ -10,8 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path - from common_helpers import get_prometheus_info diff --git a/robot-systest/resources/sa_08-vnf_with_vnf_indicators_snmp_data.py b/robot-systest/resources/sa_08-vnf_with_vnf_indicators_snmp_data.py index 27f448f..a7a3456 100644 --- a/robot-systest/resources/sa_08-vnf_with_vnf_indicators_snmp_data.py +++ b/robot-systest/resources/sa_08-vnf_with_vnf_indicators_snmp_data.py @@ -10,8 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path - from common_helpers import get_prometheus_info diff --git a/robot-systest/resources/sol003_01-vnf_lifecycle_management.py b/robot-systest/resources/sol003_01-vnf_lifecycle_management.py index a722445..a411cd2 100644 --- a/robot-systest/resources/sol003_01-vnf_lifecycle_management.py +++ b/robot-systest/resources/sol003_01-vnf_lifecycle_management.py @@ -10,8 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path - from common_helpers import get_osm_info diff --git a/tox.ini b/tox.ini index 8c9927d..729a3e0 100644 --- a/tox.ini +++ b/tox.ini @@ -23,17 +23,18 @@ toxworkdir = /tmp/.tox [testenv] usedevelop = True -basepython = python3.8 +basepython = python3.10 setenv = VIRTUAL_ENV={envdir} PYTHONDONTWRITEBYTECODE = 1 deps = -r{toxinidir}/requirements.txt +parallel_show_output = true ####################################################################################### [testenv:black] deps = black skip_install = true commands = - - black --check --diff robot-systest --fast + black --check --diff robot-systest --fast ####################################################################################### @@ -56,7 +57,7 @@ deps = {[testenv]deps} -r{toxinidir}/requirements-dev.txt pylint commands = - pylint -E robot-systest/resources + - pylint -E robot-systest/resources ####################################################################################### @@ -72,7 +73,7 @@ commands = ####################################################################################### [testenv:pip-compile] -deps = pip-tools==6.6.2 +deps = pip-tools==6.13.0 skip_install = true allowlist_externals = bash [ @@ -80,7 +81,7 @@ commands = - bash -c "for file in requirements*.in ; do \ UNSAFE="" ; \ if [[ $file =~ 'dist' ]] ; then UNSAFE='--allow-unsafe' ; fi ; \ - pip-compile -rU --no-header $UNSAFE $file ;\ + pip-compile --resolver=backtracking -rU --no-header $UNSAFE $file ;\ out=`echo $file | sed 's/.in/.txt/'` ; \ sed -i -e '1 e head -16 tox.ini' $out ;\ done"