Skip to content

Conversation

Copy link

Copilot AI commented Oct 13, 2025

Fix save() and load() functions to accept pathlib.Path objects

The issue is that save() cannot take a Path object as it expects a string. The error occurs because Path objects don't support string operations like slicing (s[-5:]) or methods like .endswith() and .lower().

Solution:

Added os.fspath() calls to convert path-like objects (including Path objects) to strings at entry points before any string operations are performed. This is the standard Python way to handle path-like objects and is compatible with both strings and Path objects.

Changes Made:

  1. In save(): Added os.fspath(filename) at the beginning to handle the .endswith('.sobj') call
  2. In load(): Added os.fspath(filename) after extracting single filename to handle the .lower() call, with logic to skip URL fetching for Path objects
  3. In _base_save(): Added os.fspath(filename) at the beginning to handle Path objects from SageObject.save()
  4. Added comprehensive doctests in both save() and load() demonstrating Path object support using context manager

The conversion is now done at all entry points (save, load, _base_save) rather than in the internal helper function (_normalize_filename), making the code cleaner and more maintainable. Additionally, Path objects are explicitly prevented from triggering URL fetching logic.

Testing:

  • Understand the issue: Path objects are not subscriptable but need to be converted to strings
  • Add os.fspath() at entry points (save, load, _base_save)
  • Keep _normalize_filename as internal string-only function
  • Add doctests to save() and load() functions to verify Path objects work
  • Update doctests to use context manager for TemporaryDirectory
  • Prevent URL fetching when Path objects are provided
  • Verify logic correctness with standalone Python tests
  • Verify syntax and indentation of changes
  • Confirm changes are minimal

The fix is minimal, follows Python best practices, and maintains backward compatibility with existing string-based code.

Original prompt

This section details on the original issue you should resolve

<issue_title>save cannot take a Path object</issue_title>
<issue_description>save cannot take a Path object, as it expects a string.

sage: one = 1
sage: from pathlib import Path
sage: save(one, Path("foobar"))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[3], line 1
----> 1 save(one, Path("foobar"))

File /Applications/sage-dev/src/sage/misc/persist.pyx:283, in sage.misc.persist.save()
    281
    282     if not os.path.splitext(filename)[1] or not hasattr(obj, 'save'):
--> 283         filename = _normalize_filename(filename)
    284
    285     if filename.endswith('.sobj'):

File /Applications/sage-dev/src/sage/misc/persist.pyx:83, in sage.misc.persist._normalize_filename()
     81 Append the .sobj extension to a filename if it doesn't already have it.
     82 """
---> 83 if s[-5:] != '.sobj':
     84     return s + '.sobj'
     85

TypeError: 'PosixPath' object is unsliceable
```</issue_description>

## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>

Fixes #1


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI changed the title [WIP] Fix save function to accept Path objects Fix save/load functions to accept pathlib.Path objects Oct 13, 2025
Copilot AI requested a review from edgarcosta October 13, 2025 16:07
Copilot finished work on behalf of edgarcosta October 13, 2025 16:07
@edgarcosta
Copy link
Owner

@copilot, follow the advice from sagemath#41037 (comment) to use context manager ("with ..") will both (a) automatically clean up the directory when you're done, and (b) give you the name to begin with. For example, this should work:

sage: with tempfile.TemporaryDirectory() as d:
....:     p = Path(d) / "test_path"
....:     save(1, p)
....:     load(p)

Copy link
Author

Copilot AI commented Oct 13, 2025

@copilot, follow the advice from sagemath#41037 (comment) to use context manager ("with ..") will both (a) automatically clean up the directory when you're do...

Updated the doctest to use a context manager as suggested. The new format is more concise and automatically handles cleanup. Changes in commit 2a8e6f1.

Copilot finished work on behalf of edgarcosta October 13, 2025 20:04
"""
Append the .sobj extension to a filename if it doesn't already have it.
"""
s = os.fspath(s) # Convert path-like objects to strings
Copy link
Owner

Choose a reason for hiding this comment

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

@copilot This line is useless, remove it

Copy link
Author

Choose a reason for hiding this comment

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

Removed the os.fspath() call from _normalize_filename and moved it to _base_save() instead. This keeps the conversion at entry points only, making _normalize_filename a simpler internal helper that works with strings. Changes in commit 90ca892.

return

filename = filename[0]
filename = os.fspath(filename) # Convert path-like objects to strings
Copy link
Owner

Choose a reason for hiding this comment

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

@copilot remove comment, add doctest, and make sure if filename is originally a Path then the branch below with fetching URL is never executed.

Copy link
Author

Choose a reason for hiding this comment

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

Removed the comment, added a doctest for Path objects in load(), and ensured Path objects skip the URL fetching branch by checking for __fspath__ before conversion. Changes in commit 6b2b9e3.

Copilot AI requested a review from edgarcosta October 14, 2025 20:57
Copilot finished work on behalf of edgarcosta October 14, 2025 20:57
Copilot finished work on behalf of edgarcosta October 14, 2025 21:01
@edgarcosta edgarcosta force-pushed the copilot/fix-save-path-object-issue branch from f916d99 to 73d5d53 Compare October 14, 2025 21:30
@edgarcosta edgarcosta force-pushed the copilot/fix-save-path-object-issue branch from 73d5d53 to 8885ac3 Compare October 14, 2025 21:31
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.

save cannot take a Path object

2 participants