-
-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Fix absolute paths for Cygwin #1970
Conversation
Absolute paths on Windows take the form "c:\somePath" -- these need to be mapped to the form "/cygdrive/c/somePath" on Cygwin. Otherwise, the virtualenv path gets garbled when activated on Cygwin, which can cause the wrong Python environment to be used.
Codecov Report
@@ Coverage Diff @@
## main #1970 +/- ##
==========================================
- Coverage 91.93% 87.83% -4.11%
==========================================
Files 174 174
Lines 8496 8474 -22
==========================================
- Hits 7811 7443 -368
- Misses 685 1031 +346
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't we already fixed this under #1940, did you check if this is still an issue with latest release?
The problem still happens with the latest release:
I saw the fix here: https://github.com/danyeaw/virtualenv/commit/927aa92d0d3cceb44c541029a0673e8c90863e92 However, this doesn't work on Cygwin, as
Even if we made that codepath run, it still wouldn't work on Cygwin, as Cygwin needs the path to begin with |
Invoking @danyeaw for clarification. Either way the solution needs to be in the python code, not the activator script though, to make it work for all shells. |
I do see what you mean. However there are a couple of issues with that:
I can't speak for all Cygwin users, but bash is the default shell, so getting back to parity with v16 for Cygwin+bash does seem like an improvement. |
cygpath is 1100 lines of cpp, I am sure it covers numerous edge cases and is going to be extremely difficult to design something in Python that covers all use cases, without reimplementing cygpath in Python. I have struggled to get even a working cygwin development environment working. Does someone want to work with me to cover more of the use cases if we don't want to call cygpath directly? |
I don't see any reason not to call cygpath, you'd just have to do it via subprocess. Why do you think you'd need to reimplement it? |
That was my initial PR, but @gaborbernat requested that I try to reimplement what was needed in Python. I attempted (although not very well apparently) to use a regex to convert the paths. It works for MSYS2, but it sounds like there are a bunch of edge cases of calling cygwin using other shells. |
@danyeaw Ah, OK. My apologies for not checking the history. Edit: Actually @gaborbernat's original comment was "if it's just a simple regex". If cygpath is 1100 lines of C++, that doesn't sound anything like a "simple regex" to me... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah if this is getting complicated let's switch to the subprocess call. Though I'm not sure if those whole 1.1k lines of C++ are needed for this change 🤔 But I'm happy to proceede with the subprocess call.
Ok, sounds good. I can submit an updated PR. Could someone volunteer to test with cygwin? |
I don't use cygwin, but hopefully @davidcoghlan can do it 🤔 Thanks! |
I don't use cygwin either, sorry. |
Hi @gaborbernat, @danyeaw, @pfmoore -- thanks for the comments! I just wanted to clarify a couple of things. In v16, the detection of whether you are in Cygwin or not happens at virtualenv activation time. In v20, it happens at virtualenv creation time. This is a behaviour change -- are you sure you prefer the new behaviour? This does have some undesirable consequences I think. Namely, if you create the virtualenv from inside Cygwin, you cannot activate it from outside Cygwin, since we've hardcoded Cygwin-style paths into the activation script. E.g., I create the virtualenv in Cygwin, I activate it using Cygwin bash; everything is fine. I later come along inside WSL and run the pre-existing activation bash script -- it won't work, since WSL doesn't understand Cygwin paths. Similarly, if we create the virtualenv from Cygwin, some of the activation scripts that are generated are inherently broken and will never work. Powershell and cmd.exe in particular don't understand Cygwin-style paths. So we've generated a set of activation scripts, but only a subset of these are valid. I'm not sure how much of a problem it will be in practice. Maybe people are okay with the fact that virtual environments created from inside Cygwin can't be used outside of it. Still, the v16 behaviour seems preferable to me. The other problem then is -- how do we detect that we're running inside Cygwin? Right now v20 checks the return value of sysconfig.get_platform() and looks for the string "cygwin". Unfortunately, this doesn't work as far as I can tell. I see "win-amd64" as the result:
In v16, the activation script checks the value of the shell variable $OSTYPE, which does seem to be reliable:
However, if we want to read this variable inside the Python code, the user would need to export it before invoking virtualenv, since it's only a shell variable. This seems like it would be a significant regression -- it would be very easy to omit this step and end up with a broken virtualenv. Coming up with some other cross-platform way of detecting Cygwin may be tricky. E.g., you could invoke uname, but this doesn't exist on regular Windows. Overall, the v16 behaviour seems preferable to me when it comes to Cygwin (hence this pull request :)) I hope that explains my reasoning anyway, even if you disagree :) |
I was going with @asottile nice summary of what is returned on each platform: It wasn't my expectations that if I mix environments that I would expect them to work properly. WSL, cygwin, msys2, cmd.exe, and PowerShell are all very different environments. |
Yeah, I think the only contract we can oblige to is that the activation script will work in the same environment you're creating it. Anything else might be stretching it. |
Hello, just to tie this one up -- I discovered the reason why
Sadly this doesn't really help me, as our environment is such that changing to the Cygwin-specific Python build isn't really possible. It's a shame that this used to work on v16 and doesn't any more, but I understand if your position on this is that you only want to support Cygwin users who are running the Cygwin-specific Python build. Closing the PR, thanks all. |
Does cygwin sets any evironment variable that would allow us to detect at creation time the user is running in cygwin? |
Hi @gaborbernat , there is a shell variable, OSTYPE, which is set to "cygwin". If you're happy to add an additional bit of code to read this, we could update our workflow to export this variable -- it's a bit non-obvious if anybody else has the same problem as us (but then perhaps nobody else does, as we're the only ones who have raised this with you :)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. In light of this maybe the solution proposed here is the best. If you reopen and replace this one with the existing one, I'm tempted to accept it 👍
Hi @gaborbernat, great! To be clear, the proposal is to go with the OSTYPE environment variable? We would also need @danyeaw 's PR here to make this work, since the regex solution doesn't return the correct paths right now. But the combination of the two changes would solve the problem for us. |
I'm happy to go ahead with the current contents of this PR, but we should also remove what we already have in place. That should suffice, not? |
For Cygwin, yes this would certainly solve it. I can't comment on mingw or msys though, I don't have any experience with those, and @danyeaw 's original fix seems to have some support in place for those. I could just take 'cygwin' off the list there, and leave that code in place for msys/mingw, which could be safer. |
@@ -34,7 +34,7 @@ def generate(self, creator): | |||
|
|||
def replacements(self, creator, dest_folder): | |||
current_platform = sysconfig.get_platform() | |||
platforms = ["mingw", "cygwin", "msys"] | |||
platforms = ["mingw", "msys"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can remove this section of code entirely if you prefer, just wondered if this would be safer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean someone needs to check if mings, msys works same/different, and if is there a cygpath there. There're also tests you need to ammend/remove.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I download MinGW/MSYS -- as I understand it, MSYS is an environment built on top of the MinGW tools (http://www.mingw.org/wiki/MSYS). The string that gets returned from sysconfig.get_platform()
depends on which flavour you install, but they are all different versions of the same thing.
Running it locally, it seems that MSYS wants paths in the form /c/somePath
, and it does not understand Cygwin-style paths:
dn_co@DESKTOP-1TDKNNU /c/Users/dn_co
$ cd /cygdrive/c/
sh: cd: /cygdrive/c/: No such file or directory
So I think the current implementation is good for MSYS/MinGW.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would suffer though from the same problem as cygwin does. Does msys has it's own variant of cygpath that we should be using to offer same guarantees we're offering for cygwin?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://stackoverflow.com/a/12063651 has some detailed answer to this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 I can confirm that MSYS has cygpath, so we can approach it in the same way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're also missing a changelog entry.
@@ -34,7 +34,7 @@ def generate(self, creator): | |||
|
|||
def replacements(self, creator, dest_folder): | |||
current_platform = sysconfig.get_platform() | |||
platforms = ["mingw", "cygwin", "msys"] | |||
platforms = ["mingw", "msys"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean someone needs to check if mings, msys works same/different, and if is there a cygpath there. There're also tests you need to ammend/remove.
@@ -46,7 +46,7 @@ deactivate () { | |||
# unset irrelevant variables | |||
deactivate nondestructive | |||
|
|||
VIRTUAL_ENV='__VIRTUAL_ENV__' | |||
VIRTUAL_ENV="$(if [ "$OSTYPE" = "cygwin" ]; then cygpath -u '__VIRTUAL_ENV__'; else echo '__VIRTUAL_ENV__'; fi;)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather have this line un-changed and have a follow-up line that normalizes the env-var (if needed). Let's not cramp things in a single line.
@@ -46,7 +46,12 @@ deactivate () { | |||
# unset irrelevant variables | |||
deactivate nondestructive | |||
|
|||
VIRTUAL_ENV="$(if [ "$OSTYPE" = "cygwin" ]; then cygpath -u '__VIRTUAL_ENV__'; else echo '__VIRTUAL_ENV__'; fi;)" | |||
VIRTUAL_ENV='__VIRTUAL_ENV__' | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for this empty line 👍
VIRTUAL_ENV='__VIRTUAL_ENV__' | ||
|
||
if [ "$OSTYPE" = "cygwin" ]; then | ||
VIRTUAL_ENV=$(cygpath -u "$VIRTUAL_ENV") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should validate that cygpath is an existing command, and ignore if it fails 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tested the updated code on cygwin & msys, and it works for both.
I've added the check to make sure cygpath exists before we invoke it. Do you mean that we should also ignore the error if invoking cygpath fails? I think we probably want to fail the operation in that case, don't we, since the virtualenv isn't going to work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Proposed fix for: #1969
Absolute paths on Windows take the form "c:\somePath" -- these need to be mapped to the form "/cygdrive/c/somePath" on Cygwin. Otherwise, the virtualenv path gets garbled when activated on Cygwin, which can cause the wrong Python environment to be used.
Thanks for contributing, make sure you address all the checklists (for details on how see
development documentation)!
tox -e fix_lint
)docs/changelog
folder