Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Treat an empty destination the same as an unspecified one #188

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

eli-schwartz
Copy link
Contributor

With a command line such as:

python -m installer --destdir="$DESTDIR" dist/*.whl

The destdir may not be "set" but is always passed. When relocating paths relative to the destination directory, the anchor is chopped off, resulting in paths that are relative to the process-wide current working directory, and are installed relative to there. Avoid doing any relocations against an empty destdir.

Fixes #136

With a command line such as:
```
python -m installer --destdir="$DESTDIR" dist/*.whl
```

The destdir may not be "set" but is always passed. When relocating paths
relative to the destination directory, the anchor is chopped off,
resulting in paths that are relative to the process-wide current working
directory, and are installed relative to there. Avoid doing any
relocations against an empty destdir.

Fixes pypa#136
@uranusjr
Copy link
Member

Personally I’d prefer this to install to the current directory; that is IMO how most commands operate. (And ultimately this is suboptimal bash programming; you should always check whether a variable is set or assume it can be empty.)

@eli-schwartz
Copy link
Contributor Author

Can you refer me to some other commands that use staging-root relocations and have the behavior you imply? Because off the top of my head all I can come up with is a long list of software that treats the semantics of a blank staging-root relocation as equivalent to unspecified, including but not limited to the one I maintain.

@eli-schwartz
Copy link
Contributor Author

eli-schwartz commented May 30, 2023

Not entirely related, but...

Keep in mind that this is a python-installer implementation of the GNU Coding Standards, which uses a standardized environment variable and environment variables are an additional layer of fun implied defaults in cross-domain tooling. Even if you don't directly read that env variable, other tooling that invokes this tool does, and has to handle both unset and set-but-blank variables.

This leads a bit into your remark about "suboptimal bash programming"...

Bash is not python and shouldn't be treated as such.

In python, a variable either is or isn't set -- in bash, a variable that isn't set, is still set anyway if it's available in os.environ, and furthermore the entire language is designed from the ground up to make heavy use of the optionality of setting variables before you refer to them. Clearing a variable is most ergonomically done by setting its value to the empty string, especially when you want to override an environment variable in a subprocess. Even checking if a variable is set cannot be portably done without a) using sentinel values, and b) passing value-or-sentinel to a subprocess (/usr/bin/test), then checking whether the subprocess raises an error. The subprocess can be optimized away using the same "as-if" rule as C++, and most (but not all) implementations do optimize it.

It's very similar to the way bash is designed from the ground up to use "exceptions" as the native control flow for if/and/while and other language intrinsics -- a command that catastrophically fails with an internal error returns 1, and so does a language intrinsic that is testing the truthiness of a factoid return 1 for "False".

There are entire books that can be written about why neither erroring commands nor unset variables can be safely assumed to mean anything in the general case. People who write a lot of shell scripts often have to explain to people who don't, why bash can't be treated like other programming languages -- perhaps the term "like good programming languages" can be used -- so this is a common point of confusion. Quite frankly, you have to be an expert to write safe bash, which is not a problem that other languages have. But if you're interested, I suggest https://mywiki.wooledge.org as a primer.

@uranusjr
Copy link
Member

uranusjr commented Jun 1, 2023

I think you’re missing my point. In most POSIX tools, an empty string means the current directly, so --destdir="" should behave the same as --destdir=".". I don’t think I recall any tool that treats passing in an empty string as path as the same as not passing a path at all, as you are proposing.

Also, off topic about the suboptimal Bash, if you search “bash best practice” anywhere, most people would tell you to do set -u.

@@ -138,7 +138,7 @@ def __init__(

def _path_with_destdir(self, scheme: Scheme, path: str) -> str:
file = os.path.join(self.scheme_dict[scheme], path)
if self.destdir is not None:
if self.destdir:
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we should handle it like this in the Python side, and instead this should be a CLI-specific thing. I'd much prefer that the CLI pass None to these objects, rather than changing the behaviour of empty strings within the API here.

I'd also be OK with disallowing empty strings in the API, just to avoid this failure mode in downstream code as well.

Copy link
Member

Choose a reason for hiding this comment

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

Also, I'd appreciate it if you could add tests for the relevant behaviour here as well. :)

@pradyunsg
Copy link
Member

Personally I’d prefer this to install to the current directory

That's the current behaviour, FWIW.

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.

Specifying destdir as empty string leads to no scripts installed
3 participants