Skip to content

Instantly share code, notes, and snippets.

@pklapperich
Last active March 13, 2024 13:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pklapperich/23bb3adb2ea97153e362c57308a3ede1 to your computer and use it in GitHub Desktop.
Save pklapperich/23bb3adb2ea97153e362c57308a3ede1 to your computer and use it in GitHub Desktop.
Using winget, conda, jq, and yq: setup a python environment using the python version from pyproject.toml for a pdm managed python project
@echo off
REM Copyright 2024 Paul Klapperich Windows Python Installer for PDM projects
REM
REM Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
REM
REM The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
REM
REM THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
REM Why??
REM on windows, *.bat files are still the least common denominator.
REM ps1 scripts require the user sets up some security settings, which a domain admin can override
REM but *.bat files always work
REM https://www.tutorialspoint.com/batch_script/index.htm
REM makes heavy use of "refreshenv" which is part of chocolatey
if "%1"=="uninstall" goto :remove_all
if "%1"=="clean" goto :clean_and_run
goto :run
:remove_all
call refreshenv
echo about to uninstall
call :Uninstall
exit /B 0
:clean_and_run
call refreshenv
call :Uninstall
:run
where /q pdm
IF %ERRORLEVEL% EQU 0 (
ECHO pdm found, trying pdm install
call :PDMInstall
IF ERRORLEVEL 1 goto :install_conda
IF ERRORLEVEL 0 goto :end
)
:: use conda to install python
:install_conda
where /q conda
IF ERRORLEVEL 1 (
ECHO conda is missing, installing from winget
winget install Anaconda.Miniconda3
call refreshenv
)
where /q pdm
IF ERRORLEVEL 1 (
ECHO pdm is missing, installing to the default conda environment
call conda install -y pdm
call refreshenv
)
:: requires jq and yq from chocolatey or from winget
:: winget doesn't need admin
:install_jq
where /q jq
IF ERRORLEVEL 1 (
ECHO jq is missing, installing from winget
winget install jqlang.jq
call refreshenv
)
where /q yq
IF ERRORLEVEL 1 (
ECHO yq is missing, installing from winget
winget install MikeFarah.yq
call refreshenv
)
call :conda_paths
call :PDMInstall
IF %ERRORLEVEL% EQU 0 goto :end
:parse_version
REM see https://packaging.python.org/en/latest/specifications/version-specifiers/
REM and https://packaging.python.org/en/latest/specifications/core-metadata/#requires-python
REM version strings may look like:
REM ==3.12.0
REM >=3.12a0
REM ==3.12.*
REM <=3.12rc2.dev345
REM
REM for any of the above, we'll just try installing 3.12, which isn't quite right, but close enough
REM
REM can also look like:
REM >= 3.12.4
REM > 3.12.4b4
REM < 3.12.4rc1.post234
REM
REM for any of the above, we'll install 3.12.4. This isn't quite right, but close enough
REM
REM Quoting rules for *.bat:
REM https://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/4095133#4095133
REM Removing the crazy quoting required by a FOR inside a .batch file, the yq/jq command would look like:
REM yq -0 -o json '.version = .project.requires-python' pyproject.toml | jq -r '.version | sub("^[^0-9]*";"") |sub("[a-zA-Z+*]{1,}.*";"") |sub("[.]$";"")'
REM
FOR /F "delims= eol=" %%e in ('"yq -o json "".version=.project.requires-python"" pyproject.toml | jq -r "".version^|sub("""^^[^^0-9]*"""^;"""""")^|sub("""[a-zA-Z+*]{1^,}.*$"""^;"""""")^|sub("""[.]$"""^;"""""")"" "' ) do (
echo pyproject.toml wants version %%e
call conda create -y --name "python%%e" python=%%e
echo Conda environment created
call :conda_paths
call :PDMInstall
)
goto end
:conda_paths
FOR /F "delims= eol=" %%e in ('"conda env list --json | jq -r ".envs[]""') do (
echo %%e
set "PATH=%%e;%PATH%"
)
EXIT /b 0
:PDMInstall
pdm self update
pdm install
EXIT /B %ERRORLEVEL%
:Uninstall
echo Uninstalling
pdm venv remove -y in-project
del /q /s .venv
FOR /F "delims= eol=" %%e in ('"conda env list --json | jq -r ".envs[]""') do (
call conda env remove --prefix %%e
)
winget remove MikeFarah.yq jqlang.jq Anaconda.Miniconda3
EXIT /B 0
:end
pdm run python
@pklapperich
Copy link
Author

pklapperich commented Mar 13, 2024

Here's an example pyproject.toml to use with this:

[project]
name = "test"
version = "0.0.1"
description = ""
authors = [
    {name = "paul.klapperich", email = "12666157+pklapperich@users.noreply.github.com"},
]
dependencies = [
    "ipython>=8.22.2",
]
requires-python = "==3.12.*"
license = {text = "MIT"}

[build-system]
requires = ["pdm-pep517>=1.0.0"]
build-backend = "pdm.pep517.api"

@pklapperich
Copy link
Author

If you can't install chocolatey, you can get the refreshenv.cmd from the chocolatey repo which is under the Apache License 2.0. Put it somewhere and put it in your path, or put it somewhere that's already in your path, such as %LOCALAPPDATA%\Microsoft\WindowsApps.

@pklapperich
Copy link
Author

Here's an alternate refreshenv script that's GPL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment