-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Enable import-module to be case insensitive #5097
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
Enable import-module to be case insensitive #5097
Conversation
fe348b9 to
31926ea
Compare
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.
If it is only for Linux maybe use "#if UNIX"?
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's technically any filesystem that's case sensitive and technically (although unlikely), it seems you can make NTFS case sensitive. for practical cases, it's probably fine to #ifdef this and if a customer has an issue on Windows, we can revisit
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.
fixed and added comment
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.
The cyclomatic complexity of this function is pretty high. Can this be refactored into a smaller function?
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 could simplify it a bit if we can make the assumption that the psd1 or psm1 file always has the same casing as the folder name. It's not clear to me if that is a safe assumption or not. The current code is conservative in that you could have: "...\Modules\FOO\foo.psd1"
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'll check with the PSGet folks
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.
per PSGet, it is safe to assume the folder and file names have the same casing. So I'll simplify this code.
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.
Please redirect to null.
New-Item -ItemType Directory -Path "$modulesPath/$modulePath" -Force > $null
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.
will add
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.
Maybe -Force to ensure you are not loading a cached version.
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.
will add
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.
actually, as part of this test, I'm removing the module with a different casing, so I don't want to -force loading
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.
Ok
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.
> $null
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.
will add
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.
> $null
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.
will add
|
@adityapatwardhan feedback addressed |
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.
In line 294 the same cycle? Can we remove it and replace with the new one on all platforms?
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 still need both as the first one will find an exact match and fall through to this if it doesn't find it. Also the above loop also handles a file path to the module which I think should be case sensitive since it's a literal file path. The lower code only handles the module name which we can treat as case insensitive. The code is similar, but I don't think it's worth combining which will make it more complex.
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.
Why we should use different logic for module folder names and module file names?
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.
If someone uses: Import-module /users/steve/dev/MyModule/MyModule.psd1 then that should be treated as a case-sensitive path. If they use Import-module mymodule, then that's case-insensitive as that's a module name and not a filepath.
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.
Ah, I meant folder and file names not paths.
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 don't see an easy way to have a single loop between the two without incurring a cost of enumerating the folders for the purpose of case-insensitive comparison in the "normal" case where either the case matches or the filesystem is case-insensitive. If there's two modules "Foo" and "foo", and the user does import-module foo we want the first loop to match foo and not Foo as that is an exact match.
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 have the same problem in #4699 - can you review the idea to use here?
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.
In the tab completion case, I think you have to cover more cases as ab might match ABc, abc, or aBcdefg, etc... so creating a case-insenstive match pattern makes sense. In this case, we have a specific name (wildcard is not accepted) and just need a case-insensitive match if we haven't already matched the exact casing (which is preferred). And we don't need to return all matches, the first one wins.
If we want just the first match to win even if there is an exact match, it would be easy to merge the two loops. However, I think we want to get an exact match if it exists, then fallback on case-sensitive search. For example, if the user typed ipmo AAA which exists in the last modulepath, but aaa exists in the first, I would have to enumerate all module folder names in every modulepath part every time just to make sure AAA wasn't found after my first case-insensitive match.
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 could have solved this situation on the other hand. Do we want all the modules to be portable? If so, we must document this and exclude the case-sensitivity in this code.
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.
That's a good point on portable scripts being able to rely on module names being case insensitive. It would make sense to just say module names are case insensitive everywhere and have it enforced by install-module and publish-module.
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.
Perhaps we could exclude #if here and leave one line.
Or maybe create helper functions - It will be easier to read and understand.
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 think this line isn't needed as I have qualifiedPath = folder above, was probably thinking this, but didn't put it together. Will fix.
d00ab71 to
43e45cf
Compare
|
Should we add negative tests too (to reflect the situations we have decided to exclude)? What errors should appear (throw)? |
|
@iSazonov test was originally skipped due to the previous code for an exact match since it wasn't possible to create two modules that differ only by casing on Windows. With the new change, no reason not to run it on Windows. I also noticed a bug in the test where I wasn't removing the test module between runs. |
|
I don't see still that tests reflect to "StringComparison.OrdinalIgnoreCase". |
|
@iSazonov the new test creates a module called TESTMODULE and imports it using testMODULE, this is where the case-insensitive comparison occurs. In the UNIX code path, if it enumerates the module folders in the module path and sees if it matches the name of the given module. Not clear to me if you were expecting a different test? |
|
@SteveL-MSFT My thoughts was that we should do `Import-Module testMODULE` and `Import-Module TESTMODULE` and check that we load the same module. Although I'm not sure it's necessary.
|
|
@iSazonov I don't think that's necessary |
simplify search loops with assumption that module file casing matches module folder name which is appropriate per PSGet
cefbc40 to
fd7dcfb
Compare
adityapatwardhan
left a comment
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.
LGTM
| { | ||
| qualifiedPath = Path.Combine(qualifiedPath, fileBaseName); | ||
| } | ||
| else if (Directory.Exists(qualifiedPath)) |
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.
Please use Utils.NativeDirectoryExists()
https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/Utils.cs#L912
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.
Will change
|
|
||
| if (!found) | ||
| #if UNIX | ||
| foreach (string folder in Directory.EnumerateDirectories(path)) |
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.
Please use Utils.NativeEnumerateDirectory()
https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/Utils.cs#L926
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.
Will change
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.
This method appears to be only implemented for Win32. Since this path is only for UNIX, I'll leave it as-is.
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.
ok
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.
Ok
Apparently macOS is case insensitive
Fix #1621