Skip to content

Fix cutout directory on Windows#1602

Merged
fneum merged 3 commits intoPyPSA:masterfrom
Eric-Nitschke:windows_cutouts
May 25, 2025
Merged

Fix cutout directory on Windows#1602
fneum merged 3 commits intoPyPSA:masterfrom
Eric-Nitschke:windows_cutouts

Conversation

@Eric-Nitschke
Copy link
Copy Markdown
Contributor

Bugfixes:

  • change CDIR in Snakefile to always contain forward slashes instead of depending on the OS.

Closes #1598

Changes proposed in this Pull Request

Checklist

  • I tested my contribution locally and it works as intended.
  • Code and workflow changes are sufficiently documented.
  • Changed dependencies are added to envs/environment.yaml.
  • Changes in configuration options are added in config/config.default.yaml.
  • Changes in configuration options are documented in doc/configtables/*.csv.
  • Sources of newly added data are documented in doc/data_sources.rst.
  • A release note doc/release_notes.rst is added.

Bugfixes:
- change CDIR in Snakefile to always contain forward slashes instead of depending on the OS.
Copy link
Copy Markdown
Member

@lkstrp lkstrp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you also try pathlib here? It is generally better for cross-platform compatibility. And we wanna leave those string paths behind.

I was under the impression that Snakemake works with "Linux-Style" paths regardless of OS

Not sure how this interferes with Snakemake. But I don't like to rely on it

@fneum
Copy link
Copy Markdown
Member

fneum commented Mar 23, 2025

I agree with @lkstrp — try pathlib. Also, the CDIR changes proposed behave differently regarding "/" suffixes. Make sure this is the same as before.

@Eric-Nitschke
Copy link
Copy Markdown
Contributor Author

Eric-Nitschke commented Mar 27, 2025

@lkstrp, @fneum I've tried it today with pathlib. Changing it to

CDIR = PurePosixPath(cutout_dir).joinpath("" if run["shared_cutouts"] else RDIR)

and changing every other occurance to

str(CDIR.joinpath("{cutout}.nc"))

(or similar), seemed to work in a dry-run.
Note that changing it back to a str is required by Snakemake, since all inputs, outputs, etc. have to be strings.
Additionally it is important to change all str concatenations to PurePosixPath.joinpath(), since the + operator is not supported between a PurePosixPath and str or another PurePosixPath.

@Eric-Nitschke
Copy link
Copy Markdown
Contributor Author

Eric-Nitschke commented Mar 28, 2025

Changing it to

CDIR = PurePosixPath(cutout_dir).joinpath("" if run["shared_cutouts"] else RDIR)

and changing every other occurance to

str(CDIR.joinpath("{cutout}.nc"))

(or similar), seemed to work in a dry-run.

It might be slighty cleaner to go with

CDIR = Path(cutout_dir, "" if run["shared_cutouts"] else RDIR)

and

CDIR.joinpath("{cutout}.nc").as_posix()

I'll try a full run with it today.

@lkstrp I've found the Snakemake pull request, that changes all Windows paths to posix paths.

Eric-Nitschke and others added 2 commits April 1, 2025 14:11
Bug fix:
- change CDIR in the Snakemake files to pathlib to properly function on Windows
Copy link
Copy Markdown
Member

@fneum fneum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a suggestion to simplify. Should hopefully also work on Windows.

regions_offshore=resources("regions_offshore.geojson"),
output:
protected(CDIR + "{cutout}.nc"),
protected(CDIR.joinpath("{cutout}.nc").as_posix()),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
protected(CDIR.joinpath("{cutout}.nc").as_posix()),
protected(CDIR / "{cutout}.nc"),

Instead of joinpath, you should be able to use the/ operator (simplifies code)

.as_posix() should also not be necessary as


In [8]: from pathlib import Path

In [9]: Path("test") / "mypath"
Out[9]: PosixPath('test/mypath')

In [10]: Path("test").joinpath("mypath")
Out[10]: PosixPath('test/mypath')

In [11]: Path("") / Path("")
Out[11]: PosixPath('.')

In [12]: Path("")
Out[12]: PosixPath('.')

In [13]: "" / Path("test")
Out[13]: PosixPath('test')

This minimises code changes in .smk files to changing "+" to "/".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @fneum,

I agree that "/" is nicer than .joinpath() (and it works just fine on Windows).

as_posix() however has to stay for two reasons:

  • a PosixPath is required to resolve issue Snakemake can't find retrieve_cutouts #1598. On Windows all your examples output a WindowsPath, which leads to the issue. Another option to ensure this, would be defining CDIR as a PurePosixPath. Pure is required because using just PosixPath does not function on Windows.
  • input, output and log files have to be strings. At least when using a PurePosixPath this always leads to errors, if the Path is not converted to a string first (either with str() or with .as_posix()). Below you will find a few error messages I got when testing it witout converting the Paths to strings or just partially converting them to strings.
RuleException in file C:[...]\pypsa-eur\rules/retrieve.smk, line 136:
Input and output files have to be specified as strings or lists of strings.

RuleException in file C:[...]\pypsa-eur\rules/retrieve.smk, line 136:
Log files have to be specified as strings.

InputFunctionException in rule build_renewable_profiles in file C:\[...]\pypsa-eur\rules/build_electricity.smk, line 306:
Error:
  Function did not return str or iterable of str. Encountered: [PurePosixPath('cutouts/europe-2013-sarah3-era5.nc')] (<class 'list'>)
``

@coroa
Copy link
Copy Markdown
Member

coroa commented Apr 30, 2025

Sorry for dropping in here late, but i was also fighting this path nonsense with snakemake a while ago. Snakemake is in principle fine with / as a path separator or the corresponding backslash \, if they are used uniformly.

ie a string may not mix between them.

The way CDIR as a string is used and compounded with other paths you are introducing in so many places "/" into the string after the CDIR that this is a loosing battle and you should just use a literal "/" as a separator.

That works reliably because snakemake runs on all paths os.path.normpath and that converts / to \ on windows.

At least that was my take-away.

Note that you can play around with an ipython on windows using wine:
(https://github.com/PietJankbal/powershell-wrapper-for-wine, and uv)

wget https://github.com/PietJankbal/powershell-wrapper-for-wine/raw/master/install_pwshwrapper.exe
wine install_pwshwrapper.exe
wine powershell

and then in the new powershell window:

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

and after reopening it:

uvx ipython

Have fun.

Copy link
Copy Markdown
Member

@fneum fneum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This version is acceptable to me! Thanks for your patience dealing with Windows paths! Iaf we find a better alternative later, we can still update. We just need Windows to run as this is 90% of first-time users.

@fneum fneum merged commit 063d8ae into PyPSA:master May 25, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Snakemake can't find retrieve_cutouts

4 participants