Skip to content

MNT: Run PTH flake test in prep for supporting pathlib (io.ascii)#16954

Merged
taldcroft merged 1 commit intoastropy:mainfrom
neutrinoceros:io.ascii/rfc/pth_checks
Sep 12, 2024
Merged

MNT: Run PTH flake test in prep for supporting pathlib (io.ascii)#16954
taldcroft merged 1 commit intoastropy:mainfrom
neutrinoceros:io.ascii/rfc/pth_checks

Conversation

@neutrinoceros
Copy link
Contributor

Description

Ref #16924
This is in the continuation of #16060

  • By checking this box, the PR author has requested that maintainers do NOT use the "Squash and Merge" button. Maintainers should respect this when possible; however, the final decision is at the discretion of the maintainer that merges the PR.

@github-actions
Copy link
Contributor

github-actions bot commented Sep 6, 2024

Thank you for your contribution to Astropy! 🌌 This checklist is meant to remind the package maintainers who will review this pull request of some common things to look for.

  • Do the proposed changes actually accomplish desired goals?
  • Do the proposed changes follow the Astropy coding guidelines?
  • Are tests added/updated as required? If so, do they follow the Astropy testing guidelines?
  • Are docs added/updated as required? If so, do they follow the Astropy documentation guidelines?
  • Is rebase and/or squash necessary? If so, please provide the author with appropriate instructions. Also see instructions for rebase and squash.
  • Did the CI pass? If no, are the failures related? If you need to run daily and weekly cron jobs as part of the PR, please apply the "Extra CI" label. Codestyle issues can be fixed by the bot.
  • Is a change log needed? If yes, did the change log check pass? If no, add the "no-changelog-entry-needed" label. If this is a manual backport, use the "skip-changelog-checks" label unless special changelog handling is necessary.
  • Is this a big PR that makes a "What's new?" entry worthwhile and if so, is (1) a "what's new" entry included in this PR and (2) the "whatsnew-needed" label applied?
  • At the time of adding the milestone, if the milestone set requires a backport to release branch(es), apply the appropriate "backport-X.Y.x" label(s) before merge.

@github-actions
Copy link
Contributor

github-actions bot commented Sep 6, 2024

👋 Thank you for your draft pull request! Do you know that you can use [ci skip] or [skip ci] in your commit messages to skip running continuous integration tests until you are ready?

output.write(outstr)
output.write(os.linesep)
output.close()
if not output:
Copy link
Member

Choose a reason for hiding this comment

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

But there is already this check above:

    if output is None:
        output = sys.stdout

Copy link
Contributor Author

@neutrinoceros neutrinoceros Sep 6, 2024

Choose a reason for hiding this comment

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

This check is different: it also invalidates the empty string '' because Path('') means "the current work directory", and this scenario (passing output='' and expecting a FileNotFoundError) is actually exercised in a test.

Copy link
Member

Choose a reason for hiding this comment

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

What was the old behavior for output=''? Does this need a change log?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Au contraire, this is being strictly backward compatible: open('') raises the same exception.

Copy link
Member

Choose a reason for hiding this comment

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

I am seeing this error message:

>>> open("", "w")
FileNotFoundError: [Errno 2] No such file or directory: ''

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'm going to revert this and simply refactor with a context manager as suggested in https://github.com/astropy/astropy/pull/16954/files#r1748045678

@pllim pllim added no-changelog-entry-needed Extra CI Run cron CI as part of PR labels Sep 6, 2024
Copy link
Member

@taldcroft taldcroft left a comment

Choose a reason for hiding this comment

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

Looks OK but with requested changes. Overall in the Path migration I think you should mostly avoid doing a blind replacement of open(...) with Path().read/write_text().

There is nothing wrong with open() and the two cases here show that open() is often preferable because it is a streaming interface.

Comment on lines +141 to +142
elif isinstance(qdp_file, (str, os.PathLike)):
lines = [line.strip() for line in Path(qdp_file).read_text().splitlines()]
Copy link
Member

Choose a reason for hiding this comment

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

This new code ends up making two additional copies of the entire file in memory, one the full read_text() output and second the splitlines() output. In general splitlines() is not a drop-in replacement for iterating over open() since splitlines() removes the line endings. In this case it doesn't matter but best to keep this Path refactoring as close to the original code as possible.

So basically the only change here should be isinstance(qdp_file, (str, os.PathLike)):.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this case it doesn't matter but best to keep this Path refactoring as close to the original code as possible.

100% agreed. Any behavioral difference with the original code that I don't advertise myself in a comment is a mistake from me, so thank you for your attention to details !

output.write(outstr)
output.write(os.linesep)
output.close()
if not output:
Copy link
Member

Choose a reason for hiding this comment

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

I am seeing this error message:

>>> open("", "w")
FileNotFoundError: [Errno 2] No such file or directory: ''

if isinstance(output, (str, bytes, os.PathLike)):
output = os.path.expanduser(output)
if isinstance(output, (str, bytes, os.PathLike)) and output:
output = Path(output).expanduser()
Copy link
Member

Choose a reason for hiding this comment

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

I would prefer to revert the changes In this function and stick with the original os.path.expanduser(). In this case using Path is introducing new complexity to the logic because of the Path("") => cwd() behavior. So maybe add a #noqa ....

# Path("") gives the current working directory
raise FileNotFoundError("No such file or directory")
Path(output).write_text(
outstr + os.linesep,
Copy link
Member

Choose a reason for hiding this comment

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

This creates an unnecessary temporary string outstr + os.linesep.

Comment on lines -1062 to -1065
output = open(output, "w", newline="")
output.write(outstr)
output.write(os.linesep)
output.close()
Copy link
Member

Choose a reason for hiding this comment

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

This should just be changed to an open(...) context manager.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -379,7 +380,7 @@ def read(self, table):
with suppress(TypeError):
# For strings only
if os.linesep not in table + "":
Copy link
Member

Choose a reason for hiding this comment

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

@taldcroft just out of pure curiosity, what is the purpose of the + "" here?

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 assume this is to trigger a TypeError if table isn't a string ?

Copy link
Member

Choose a reason for hiding this comment

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

Exactly what @neutrinoceros said. This is a very old idiom to check for string-like (somewhat like a protocol, asking if it supports appending a str) instead of an explicit str type check.

What's the best way to check for string-like? Or does isinstance(table, str) cover all realistic use cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

isinstance is definitely more idiomatic here; I can't think of any type other than str that accepts another str in __add__

Copy link
Contributor

@dhomeier dhomeier Sep 9, 2024

Choose a reason for hiding this comment

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

From BaseReader.read in core.py below, which uses the same construct, the only other input cases are file-like or list of strings (the table lines), so they should all be covered.

@neutrinoceros
Copy link
Contributor Author

Overall in the Path migration I think you should mostly avoid doing a blind replacement of open(...) with Path().read/write_text().

There is nothing wrong with open() and the two cases here show that open() is often preferable because it is a streaming interface.

@taldcroft I agree. I'll open a PR to propose ignoring that specific rule (PTH123) more permanently. Moving this one to draft until it is resolved.

@neutrinoceros neutrinoceros marked this pull request as draft September 9, 2024 09:16
@neutrinoceros neutrinoceros changed the title MNT: Run PTH flake test in prep for supporting pathlib (io.ascii) MNT: Run PTH flake test in prep for supporting pathlib (io.ascii) (⏰ wait for #16972) Sep 9, 2024
@neutrinoceros neutrinoceros force-pushed the io.ascii/rfc/pth_checks branch from 48210e4 to f897605 Compare September 10, 2024 07:09
@neutrinoceros neutrinoceros changed the title MNT: Run PTH flake test in prep for supporting pathlib (io.ascii) (⏰ wait for #16972) MNT: Run PTH flake test in prep for supporting pathlib (io.ascii) Sep 10, 2024
@neutrinoceros
Copy link
Contributor Author

I think I've now addressed all comments from @taldcroft, let me know if there's anything I missed !

@neutrinoceros neutrinoceros marked this pull request as ready for review September 10, 2024 07:10
with open(output, "w", newline="") as output:
output.write(outstr)
output.write(os.linesep)
output.close()
Copy link
Member

Choose a reason for hiding this comment

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

The .close() is not needed with the context manager.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching this. Fixed.

@neutrinoceros neutrinoceros force-pushed the io.ascii/rfc/pth_checks branch from f897605 to e04f9b3 Compare September 12, 2024 10:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants