Skip to content

Conversation

@SteveL-MSFT
Copy link
Member

@SteveL-MSFT SteveL-MSFT commented Oct 12, 2017

Apparently macOS is case insensitive

Fix #1621

Copy link
Collaborator

@iSazonov iSazonov Oct 13, 2017

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"?

Copy link
Member Author

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

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed and added comment

Copy link
Member

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?

Copy link
Member Author

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"

Copy link
Member Author

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

Copy link
Member Author

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.

Copy link
Member

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

Copy link
Member Author

Choose a reason for hiding this comment

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

will add

Copy link
Member

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.

Copy link
Member Author

Choose a reason for hiding this comment

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

will add

Copy link
Member Author

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

Copy link
Member

Choose a reason for hiding this comment

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

Ok

Copy link
Member

Choose a reason for hiding this comment

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

> $null

Copy link
Member Author

Choose a reason for hiding this comment

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

will add

Copy link
Member

Choose a reason for hiding this comment

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

> $null

Copy link
Member Author

Choose a reason for hiding this comment

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

will add

@SteveL-MSFT
Copy link
Member Author

@adityapatwardhan feedback addressed

Copy link
Collaborator

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?

Copy link
Member Author

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.

Copy link
Collaborator

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?

Copy link
Member Author

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.

Copy link
Collaborator

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.

Copy link
Member Author

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.

Copy link
Collaborator

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?

Copy link
Member Author

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.

Copy link
Collaborator

@iSazonov iSazonov Oct 15, 2017

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.

Copy link
Member Author

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.

Copy link
Collaborator

@iSazonov iSazonov Oct 16, 2017

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.

Copy link
Member Author

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.

@SteveL-MSFT SteveL-MSFT force-pushed the import-module-casing branch 2 times, most recently from d00ab71 to 43e45cf Compare October 16, 2017 18:24
@iSazonov
Copy link
Collaborator

iSazonov commented Oct 17, 2017

Should we add negative tests too (to reflect the situations we have decided to exclude)? What errors should appear (throw)?
Also why we skip Windows in new test? If we want to have all modules portable all test should run on all platforms identically.

@SteveL-MSFT
Copy link
Member Author

@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.

@iSazonov
Copy link
Collaborator

I don't see still that tests reflect to "StringComparison.OrdinalIgnoreCase".

@SteveL-MSFT
Copy link
Member Author

@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?

@iSazonov
Copy link
Collaborator

iSazonov commented Oct 17, 2017 via email

@SteveL-MSFT
Copy link
Member Author

@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
combine loops
removed test that is no longer valid
enable test for Windows, removed invalid test where a module with version folder requires a manifest
Copy link
Member

@adityapatwardhan adityapatwardhan left a 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))
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

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))
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

Will change

Copy link
Member Author

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.

Copy link
Member

Choose a reason for hiding this comment

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

ok

Copy link
Member

Choose a reason for hiding this comment

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

Ok

changed to using Utils.NativeDirectoryExists() for perf reasons
@adityapatwardhan adityapatwardhan merged commit e908b8a into PowerShell:master Oct 19, 2017
@SteveL-MSFT SteveL-MSFT deleted the import-module-casing branch November 12, 2017 05:17
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.

3 participants