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

Tweaks for Windows portability #201

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

jvolkman
Copy link
Contributor

@jvolkman jvolkman commented Feb 6, 2024

This is the other half of the patch set I've been maintaining for repairwheel. Hopefully these changes are innocuous enough to push upstream.

Don't assume we're running on a system with POSIX-style paths.
Comment on lines +213 to +216
# Make copied file writeable if necessary.
statinfo = os.stat(new_path)
if not statinfo.st_mode & stat.S_IWRITE:
os.chmod(new_path, statinfo.st_mode | stat.S_IWRITE)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This can also be found in auditwheel

@@ -40,7 +41,11 @@ class DependencyNotFound(Exception):
"""


def _filter_system_libs(libname: Text) -> bool:
def filter_system_libs(libname: Text) -> bool:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was duplicated between here and delocating.py, so I removed the latter in favor of this copy.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Moving filter_system_libs like this technically breaks the API. Were you using it in your own library?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, that's a good point. Given the circular import issues between these to files, I guess I can just revert back to the duplication.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This function is really showing its age by accepting a Text input. I suspect it actually works with POSIX paths exclusively. Did something happen to cause this refactor?

I've very hesitant to accept a PR with os.path.splitdrive.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All of these changes are the result of repairing macOS wheels on a Windows host.

If I remember correctly, callers of filter_system_libs are inquiring about, for example, C:\usr\lib\libSystem.B.dylib - which obviously doesn't exist on the repairing host - but we can still consider it as a system library. So maybe the proper fix is elsewhere upstream.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm very interested in learning what's putting C: into C:\usr\lib\libSystem.B.dylib.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can try to reproduce the issue again tomorrow. Unfortunately I don't actually have a Windows machine so it's a lot of pushing and waiting for CI. :(

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, since you've reopened this:

I'm very interested in learning what's putting C: into C:\usr\lib\libSystem.B.dylib.

It comes from here. On windows, realpath("foo") == "C:\\foo", where C:\ is the drive letter or UNC path of the current working directory. So if you chdir into \\myserver\shared_dir, then realpath("foo") == "\\\\myserver\\shared_dir\\foo"

Copy link
Collaborator

Choose a reason for hiding this comment

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

Good work. I think realpath should not be called on non-existing files in this case.

If I changed the return types for search_environment_for_lib then I'd use Path for existing files and PurePosixPath for missing files.

@jvolkman jvolkman marked this pull request as ready for review February 6, 2024 04:23
@HexDecimal
Copy link
Collaborator

Honestly I think the whole source should be converted to use pathlib's Path and PurePosixPath.

@HexDecimal
Copy link
Collaborator

This kind of stuff I want to revisit by adding Ruff's PTH rule. I don't want these outdated calls to be refactored into other outdated calls.

@jvolkman
Copy link
Contributor Author

jvolkman commented Feb 6, 2024

Moving to pathlib sounds great.

I was interested to see that there are CI tests running on windows. I don't know the full history of this project but I guess there was some cross-platform consideration at some point.

@HexDecimal
Copy link
Collaborator

I was interested to see that there are CI tests running on windows. I don't know the full history of this project but I guess there was some cross-platform consideration at some point.

There are a few issues such as #29 and some others about using cross platform tools. I added Windows testing at least to keep track of which features were portable, but most of them rely on either MacOS tools or linkage.

Copy link
Collaborator

@HexDecimal HexDecimal left a comment

Choose a reason for hiding this comment

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

Recommended reading: https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module

I don't think now is the time for a major refactor. Just fix what's absolutely needed.

Comment on lines 61 to 64
def posix_relpath(path: str, start: str = None) -> str:
"""Return path relative to start using posix separators (/)."""
rel = relpath(path, start)
return Path(rel).as_posix()
Copy link
Collaborator

Choose a reason for hiding this comment

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

If the goal is cross platform portability then do not use str for path types in new functions.

@@ -157,7 +165,7 @@ def _analyze_tree_libs(
# @rpath, etc, at this point should never happen.
raise DelocationError("%s was expected to be resolved." % required)
r_ed_base = basename(required)
if relpath(required, rp_root_path).startswith(".."):
if Path(rp_root_path) not in Path(required).parents:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if Path(rp_root_path) not in Path(required).parents:
if not Path(required).is_relative_to(rp_root_path):

Copy link
Contributor Author

Choose a reason for hiding this comment

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

is_relative_to is new in 3.9 and it looks like you're testing back to 3.7.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oops. That's easy to miss when I'm not using an IDE.

Python 3.8 EOL is this year. Maybe I can justify dropping support for it? The path in path.parents syntax looks terrible even compared to relpath(...).startswith("..").

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I suppose one could say the syntax isn't great, but the docs do list root in path.parents as equivalent to is_relative_to.

@@ -40,7 +41,11 @@ class DependencyNotFound(Exception):
"""


def _filter_system_libs(libname: Text) -> bool:
def filter_system_libs(libname: Text) -> bool:
Copy link
Collaborator

Choose a reason for hiding this comment

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

This function is really showing its age by accepting a Text input. I suspect it actually works with POSIX paths exclusively. Did something happen to cause this refactor?

I've very hesitant to accept a PR with os.path.splitdrive.

@HexDecimal
Copy link
Collaborator

I can try to reproduce the issue again tomorrow. Unfortunately I don't actually have a Windows machine so it's a lot of pushing and waiting for CI. :(

True Windows support is going to be a difficult task. It'll be far more complex than making sure paths work correctly. I suggest closing this for now and not worrying about Windows support for the foreseeable future.

@HexDecimal
Copy link
Collaborator

I apologize, but I'd like to put any Windows specific refactoring on hold for now. At least until certain Ruff rules are enforced and older versions of Python are dropped.

@HexDecimal HexDecimal closed this Feb 7, 2024
@HexDecimal
Copy link
Collaborator

HexDecimal commented Feb 7, 2024

You can still make a PR for refactors needed by repairwheel, if necessary.

@jvolkman
Copy link
Contributor Author

jvolkman commented Feb 7, 2024

True Windows support is going to be a difficult task. It'll be far more complex than making sure paths work correctly.

This is true, but I've already tackled the more complicated stuff in repairwheel by reimplementing otool, install_name_tool, and codesign in Python. But I'm able to more easily patch those changes in at runtime (here).

Unless you're talking about delocating Windows dlls, for which there is already delvewheel.

@HexDecimal
Copy link
Collaborator

I've already tackled the more complicated stuff in repairwheel by reimplementing otool, install_name_tool, and codesign in Python.

Then you've already finished the hardest parts. Never mind my pessimism, it sounds like you've got it handled.

The reason I closed this is because your changes were causing readability and future refactoring concerns, such as is_resolved_subpath not being documented as a workaround to a missing Path.is_relative_to. I want to avoid the code being full of posix_relpath and is_resolved_subpath style workarounds. This would likely mean dropping Python 3.8 support before continuing, and adding linter rules to enforce the use of pathlib. These updates would conflict with this PR.

I'm handing another large PR that I'd like to merge before working on this one.

I'll reopen this to keep track of it, but it's possible that you may have to start over at a later time.

@HexDecimal HexDecimal reopened this Feb 7, 2024
Copy link

codecov bot commented Feb 7, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (eaba43d) 96.45% compared to head (8ffe7ac) 96.47%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #201      +/-   ##
==========================================
+ Coverage   96.45%   96.47%   +0.02%     
==========================================
  Files          15       15              
  Lines        1184     1192       +8     
==========================================
+ Hits         1142     1150       +8     
  Misses         42       42              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

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.

2 participants