diff --git a/.circleci/config.yml b/.circleci/config.yml index 9743973..1f0ff77 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,3 +1,40 @@ +orbs: + win: circleci/windows@2.2.0 + +windows-tmpl: &windows-tmpl + parameters: + python-version: + type: string + executor: + name: win/default + shell: bash.exe + steps: + - checkout + - run: + name: Set up Python + command: | + set -e + . install.sh + install_windows_make + install_windows_python << parameters.python_version >> + init_venv python + - run: + name: Install dependencies + command: | + python --version + make develop + - run: + name: Run tests + command: | + export DEBUG=1 + export SERVER_FIXTURES_JENKINS_WAR= + export PACKAGES=$(./foreach.sh --quiet 'grep -q Windows setup.py && echo $PKG || true') + make test-ci + - store_test_results: + path: junit + - run: + name: Check for failures + command: make list-test-failures test-tmpl: &test-tmpl command: | @@ -152,47 +189,22 @@ job-tmpl: &job-tmpl - ./* - ./dist/* -version: 2 +version: 2.1 jobs: - py36: + python-ubuntu: <<: *job-tmpl + parameters: + python_version: + type: string environment: - PYTHON: "python3.6" - - py37: - <<: *job-tmpl - environment: - PYTHON: "python3.7" - - py38: - <<: *job-tmpl - environment: - PYTHON: "python3.8" - - py39: - <<: *job-tmpl - environment: - PYTHON: "python3.9" - - py310: - <<: *job-tmpl - environment: - PYTHON: "python3.10" - - py311: - <<: *job-tmpl - environment: - PYTHON: "python3.11" - - py312: - <<: *job-tmpl - environment: - PYTHON: "python3.12" - - py313: - <<: *job-tmpl + PYTHON: << parameters.python_version >> + python-windows: + <<: *windows-tmpl + parameters: + python_version: + type: string environment: - PYTHON: "python3.13" + PYTHON: << parameters.python_version >> pypi-release: docker: @@ -253,38 +265,41 @@ workflows: version: 2 pytest-plugins: jobs: - - py36 - - py37 - - py38 - - py39 - - py310 - - py311 - - py312 - - py313 + - python-windows: + matrix: + parameters: + python_version: + - "python3.6" + - "python3.7" + - "python3.8" + - "python3.9" + - "python3.10" + - "python3.11" + - "python3.12" + - python-ubuntu: + matrix: + parameters: + python_version: + - "python3.6" + - "python3.7" + - "python3.8" + - "python3.9" + - "python3.10" + - "python3.11" + - "python3.12" + - "python3.13" - pypi-release: requires: - - py36 - - py37 - - py38 - - py39 - - py310 - - py311 - - py312 - - py313 + - python-ubuntu + - python-windows filters: branches: only: - master - publish-github-release: requires: - - py36 - - py37 - - py38 - - py39 - - py310 - - py311 - - py312 - - py313 + - python-ubuntu + - python-windows filters: branches: only: diff --git a/.travis.yml b/.travis.yml deleted file mode 100755 index 95edb67..0000000 --- a/.travis.yml +++ /dev/null @@ -1,66 +0,0 @@ -language: sh - -os: - - windows - -python: - - "2.7" - - "3.5" - - "3.6" - - "3.7" - -jobs: - include: - - name: "Python 2.7 on Windows" - python: "2.7" - before_install: - - set -e - - . install.sh - - install_windows_make - - install_windows_py27 - - init_venv python - - name: "Python 3.5 on Windows" - python: "3.5" - before_install: - - set -e - - . install.sh - - install_windows_make - - install_windows_py35 - - init_venv python - - name: "Python 3.6 on Windows" - python: "3.6" - before_install: - - set -e - - . install.sh - - install_windows_make - - install_windows_py36 - - init_venv python - - name: "Python 3.7 on Windows" - python: "3.7" - before_install: - - set -e - - . install.sh - - install_windows_make - - install_windows_py37 - - init_venv python - -install: - - python --version - - make develop -before_script: - - export DEBUG=1 - - export SERVER_FIXTURES_JENKINS_WAR= - # Select all packages with the Windows classifier - - export PACKAGES=$(./foreach.sh --quiet 'grep -q Windows setup.py && echo $PKG || true') -script: - - make test-ci -after_script: - - bash -c "! compgen -G 'FAILED-*'" - -cache: - directories: - - ~/AppData/Local/pip/Cache - -git: - depth: false - submodules: false diff --git a/CHANGES.md b/CHANGES.md index 2ab4659..7b65a22 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ ## Changelog -### 1.8.0 (2024-10-??) +### 1.8.1 (2024-10-??) + * All: Windows builds added to CircleCI + * All: Started building py3.6-py3.13 in CircleCI + +### 1.8.0 (2024-10-17) * All: Drop support for Python 2 and <3.6, removing compatibility code. * All: Use stdlib unittest.mock instead of mock package. * All: Removed usage of path.py and path in favour of pathlib. #174 #224 diff --git a/Makefile b/Makefile index 318caad..0e8ed16 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,18 @@ test: test-ci: rm -f FAILED-* - ./foreach.sh 'cat *.egg-info/top_level.txt | xargs -Ipysrc coverage run -p --source=pysrc setup.py test -sv -ra --timeout 120 || touch ../FAILED-$$PKG' + mkdir junit + ./foreach.sh 'cat *.egg-info/top_level.txt | xargs -Ipysrc coverage run -p --source=pysrc -m pytest --junitxml junit.xml -svvvv -ra || touch ../FAILED-$$PKG' + ./foreach.sh 'cp junit.xml ../junit/junit-$PKG.xml || true' + +list-test-failures: + @if compgen -G 'FAILED-*' > /dev/null; then \ + echo "Error: Found failure artifacts:"; \ + compgen -G 'FAILED-*'; \ + exit 1; \ + else \ + echo "No failure artifacts found."; \ + fi upload: pip install twine diff --git a/README.md b/README.md index 5982405..e8af886 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # A goody-bag of nifty plugins for [pytest](https://pytest.org) -OS | Build | Coverage | - ------ | ----- | -------- | - ![Linux](img/linux.png) | [![CircleCI (Linux)](https://circleci.com/gh/man-group/pytest-plugins/tree/master.svg?style=svg)](https://circleci.com/gh/man-group/pytest-plugins/tree/master) | [![Coverage Status](https://coveralls.io/repos/github/manahl/pytest-plugins/badge.svg?branch=master)](https://coveralls.io/github/manahl/pytest-plugins?branch=master) - ![Windows](img/windows.png) | [![Travic CI (Windows)](https://travis-ci.org/man-group/pytest-plugins.svg?branch=master)](https://travis-ci.org/man-group/pytest-plugins) | +OS | Build | Coverage | + ------ |------------------------------------------------------------------------------------------------------------------------------------------------------------------| -------- | + ![Linux](img/linux.png) | [![CircleCI (Linux)](https://circleci.com/gh/man-group/pytest-plugins/tree/master.svg?style=svg)](https://circleci.com/gh/man-group/pytest-plugins/tree/master) | [![Coverage Status](https://coveralls.io/repos/github/manahl/pytest-plugins/badge.svg?branch=master)](https://coveralls.io/github/manahl/pytest-plugins?branch=master) + ![Windows](img/windows.png) | [![CircleCI (Linux)](https://circleci.com/gh/man-group/pytest-plugins/tree/master.svg?style=svg)](https://circleci.com/gh/man-group/pytest-plugins/tree/master) | Plugin | Description | Supported OS | ------ | ----------- | ------------ | diff --git a/install.sh b/install.sh index 7790879..5e448f2 100644 --- a/install.sh +++ b/install.sh @@ -55,7 +55,6 @@ function install_python { function choco_install { local args=$* - # choco fails randomly with network errors on travis, have a few goes for i in {1..5}; do choco install $args && return 0 echo 'choco install failed, log tail follows:' @@ -71,35 +70,26 @@ function install_windows_make { choco_install make --params "/InstallDir:C:\\tools\\make" } - -function install_windows_py27 { - choco_install python2 --params "/InstallDir:C:\\Python" - export PATH="/c/Python:/c/Python/Scripts:$PATH" - install_python_packaging python -} - - -function install_windows_py35 { - choco_install python --version 3.5.4 --params "/InstallDir:C:\\Python" - export PATH="/c/Python35:/c/Python35/Scripts:$PATH" - install_python_packaging python +function install_windows_python() { + if [ -z "$1" ]; then + echo "Please provide a Python version argument, e.g., 'python3.11'" + return 1 + fi + python_arg="$1" + python_version="${python_arg#python}" + major_version="${python_version%%.*}" + minor_version="${python_version#*.}" + choco_package="python${major_version}${minor_version}" + install_dir="/c/Python${major_version}${minor_version}" + choco_install "$choco_package" --params "/InstallDir:C:\\Python" -y + if [ $? -ne 0 ]; then + echo "Failed to install Python $python_version" + return 1 + fi + export PATH="$install_dir:$install_dir/Scripts:$PATH" + install_python_packaging python } - -function install_windows_py36 { - choco_install python --version 3.6.8 --params "/InstallDir:C:\\Python" - export PATH="/c/Python36:/c/Python36/Scripts:$PATH" - install_python_packaging python -} - - -function install_windows_py37 { - choco_install python --version 3.7.5 --params "/InstallDir:C:\\Python" - export PATH="/c/Python37:/c/Python37/Scripts:$PATH" - install_python_packaging python -} - - function init_venv { local py=$1 virtualenv venv --python=$py diff --git a/pytest-virtualenv/pytest_virtualenv.py b/pytest-virtualenv/pytest_virtualenv.py index 74b2873..9257454 100644 --- a/pytest-virtualenv/pytest_virtualenv.py +++ b/pytest-virtualenv/pytest_virtualenv.py @@ -154,7 +154,7 @@ def __init__(self, env=None, workspace=None, name='.env', python=None, args=None self.pip_version = self._get_pip_version() def _get_pip_version(self) -> Tuple[int, ...]: - output = self.run([self.python, self.pip, '--version'], capture=True) + output = self.run([self.python, "-m", "pip", "--version"], capture=True) version_number_strs = output.split(" ")[1].split(".") return tuple(map(int, version_number_strs)) @@ -203,7 +203,7 @@ def install_package(self, pkg_name, version=PackageVersion.LATEST, installer="pi """ if sys.platform == 'win32': # In virtualenv on windows "Scripts" folder is used instead of "bin". - installer = str(self.virtualenv / 'Scripts' / installer + '.exe') + installer = str(self.virtualenv / 'Scripts' / installer) + '.exe' else: installer = str(self.virtualenv / 'bin' / installer) if not self.debug: @@ -268,7 +268,7 @@ def installed_packages(self, package_type=None): "for i in metadata.distributions(): print(i.name + ' ' + i.version + ' ' + str(i.locate_file('')))" lines = self.run([self.python, "-c", code], capture=True).split('\n') for line in [i.strip() for i in lines if i.strip()]: - name, version, location = line.split() + name, version, location = line.split(" ", 2) res[name] = PackageEntry(name, version, location) return res @@ -280,9 +280,13 @@ def _install_importlib_metadata(self): def _install_package_from_editable_egg_link(self, egg_link, package): import pkg_resources - python_dir = "python{}.{}".format(sys.version_info.major, sys.version_info.minor) - shutil.copy(egg_link, self.virtualenv / "lib" / python_dir / "site-packages" / egg_link.name) - easy_install_pth_path = self.virtualenv / "lib" / python_dir / "site-packages" / "easy-install.pth" + if sys.platform == "win32": + shutil.copy(egg_link, self.virtualenv / "Lib" / "site-packages" / egg_link.name) + easy_install_pth_path = self.virtualenv / "Lib" / "site-packages" / "easy-install.pth" + else: + python_dir = "python{}.{}".format(sys.version_info.major, sys.version_info.minor) + shutil.copy(egg_link, self.virtualenv / "lib" / python_dir / "site-packages" / egg_link.name) + easy_install_pth_path = self.virtualenv / "lib" / python_dir / "site-packages" / "easy-install.pth" with open(easy_install_pth_path, "a") as pth, open(egg_link) as egg_link: pth.write(egg_link.read()) pth.write("\n") @@ -292,6 +296,7 @@ def _install_package_from_editable_egg_link(self, egg_link, package): if dependency and (not dependency.marker or dependency.marker.evaluate()): self.install_package(dependency.name, version=PackageVersion.CURRENT) + def _normalize(name): return re.sub(r"[-_.]+", "-", name).lower() @@ -303,6 +308,7 @@ def _get_egg_link(package_name): return egg_link return None + def _get_editable_package_location_from_direct_url(package_name: str) -> Optional[str]: """ Uses the PEP610 direct_url.json to get the installed location of a given