This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: pathlib Path objects should support __format__
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.8
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: alexia, brett.cannon, eric.smith, miss-islington, pitrou, serhiy.storchaka, steve.dower, tebeka
Priority: normal Keywords: patch

Created on 2019-09-19 06:01 by tebeka, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 16286 closed python-dev, 2019-09-19 15:48
PR 16287 closed python-dev, 2019-09-19 15:51
PR 25542 miss-islington, 2021-04-22 23:36
Messages (16)
msg352771 - (view) Author: Miki Tebeka (tebeka) * Date: 2019-09-19 06:01
Currently pathlib.Path cannot be used with string formatting directives. IMO it should.

>>> from pathlib import Path
>>> path = Path('/path/to/enlightenment')
>>> print(f'path is: {path:>50}')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported format string passed to PosixPath.__format__
msg352772 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-09-19 07:26
Convert it to string first:

    print(f'path is: {path!s:>50}')
msg352773 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2019-09-19 08:16
I agree with Serhiy that !s solves the problem. But as a convenience it might be nice to add __format__. This issue pops up from time to time on Path and other types, and I don't think !s is very discoverable. Especially because formatting works with a specifier, but fails with one:

>>> format(path)
'/path/to/enlightenment'
>>> format(path, '>50')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported format string passed to PosixPath.__format__
msg352775 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-09-19 09:02
Initially, the default __format__ converted the object to str and passed format specifier to str.__format__. This is defined in PEP 3101:

class object:
    def __format__(self, format_spec):
        return format(str(self), format_spec)

But later we changed the implementation, because this leaded to difficult to catch errors. Non-empty format specifier is now error in the default __format__.
msg352784 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2019-09-19 10:04
Correct: this change was made specifically so that objects could add their own format specifiers at a later time. If I recall correctly, This change was precipitated by wanting to add datetime.__format__: this change broke existing uses for format() that formatted datetimes as strings.

But the result is that objects that really just want to be formatted as strings need to have their own __format__ which returns format(str(self), format_spec).
msg352785 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-09-19 10:18
Or use !s or !r.

In some cases it may be even better to use both convertions: f'{str(path)!r:>50}'.

This is all application specific, so I do not think we should rethink our old decision. Explicit is better than implicit.
msg352787 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2019-09-19 12:12
I don't think anyone is suggesting reverting the decision on object.__format__. That decision should stay.

I think the suggestion is to add Path.__format__, which just converts to a string and formats with the given spec.

Unless someone can think of a reason to have a different format specifier language for Path, I think this is a good usability improvement.
msg352794 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-09-19 14:31
I am -0 on this. It is a can of worms. Once we add a trivial __format__ in Path, we will need to handle numerous requests for adding a trivial __format__ in many other classes. It is better to teach users to use !s (and this works also on older Python versions!).

As for Path, there many ways to convert it to string: str(), repr(), os.fspath(), os.fsdecode(), Path.as_posix(), Path.as_uri(), with possible normalization, converting to absolute or relative path. I would not bet that we will never have reasons to have a different format specifier language for Path.
msg352795 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2019-09-19 14:33
I'm also -0 on it. It's up to Antoine.
msg352801 - (view) Author: Miki Tebeka (tebeka) * Date: 2019-09-19 15:45
I don't think it violates " Explicit is better than implicit."
There's a lot of work done to make pathlib.Path objects work in places where str or bytes is expected (e.g PEP 519), IMO this is another case.
msg352814 - (view) Author: Brett Cannon (brett.cannon) * (Python committer) Date: 2019-09-19 20:54
I'm -1 as PEP 519 created __fspath__ specifically so that pathlib.Path wouldn't be confused as a string by accident. You can also use os.fspath() to get a string representation of the path itself.

I also don't know if Antoine wants to make this sort of call for pathlib anymore as he isn't listed in https://devguide.python.org/experts/ or https://github.com/python/cpython/blob/master/.github/CODEOWNERS for pathlib anymore (and that was an explicit choice based on code history for the experts index).
msg391640 - (view) Author: Steve Dower (steve.dower) * (Python committer) Date: 2021-04-22 23:30
New changeset e07d8098892e85ecc56969d2c9a5afb3ea33ce8f by Steve Dower in branch 'master':
bpo-38222: Check specifically for a drive, not just a colon (GH-25540)
https://github.com/python/cpython/commit/e07d8098892e85ecc56969d2c9a5afb3ea33ce8f
msg401710 - (view) Author: (alexia) Date: 2021-09-13 16:39
I would like for this to be reconsidered. Yes, you can use str(), but converting back and forth becomes really clunky:

log_dir = 'logs/{date}'
log_file = Path(str(path).format(time.strftime('%Y-%m-%d')) / 'log.txt'
msg401711 - (view) Author: (alexia) Date: 2021-09-13 16:39
Sorry, that should have been:

log_dir = Path('logs/{date}')
msg401712 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2021-09-13 16:47
> log_dir = Path('logs/{date}')
> log_file = Path(str(path).format(time.strftime('%Y-%m-%d')) / 'log.txt'

(I think you're missing "date=" in the call to .format().)

Could you show what you think this would look like if Path.__format__ existed? I don't think anyone's suggesting that path.format() would be added by this issue, but maybe that would be a reasonable proposal as a different issue.
msg401721 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2021-09-13 17:37
I would be in favour of adding Path.__format__, even though I'm not terribly convinced by the use case presented.
History
Date User Action Args
2022-04-11 14:59:20adminsetgithub: 82403
2021-09-13 17:37:51pitrousetmessages: + msg401721
2021-09-13 16:47:19eric.smithsetmessages: + msg401712
2021-09-13 16:39:59alexiasetmessages: + msg401711
2021-09-13 16:39:06alexiasetnosy: + alexia
messages: + msg401710
2021-04-22 23:36:31miss-islingtonsetpull_requests: + pull_request24267
2021-04-22 23:35:18steve.dowersetpull_requests: - pull_request24264
2021-04-22 23:35:17steve.dowersetpull_requests: - pull_request24263
2021-04-22 23:30:55miss-islingtonsetpull_requests: + pull_request24264
2021-04-22 23:30:47miss-islingtonsetnosy: + miss-islington

pull_requests: + pull_request24263
2021-04-22 23:30:45steve.dowersetmessages: + msg391640
2021-04-22 22:39:38steve.dowersetpull_requests: - pull_request24260
2021-04-22 22:38:31steve.dowersetnosy: + steve.dower

pull_requests: + pull_request24260
2019-09-19 20:54:43brett.cannonsetstatus: open -> closed

nosy: + brett.cannon
messages: + msg352814

resolution: rejected
stage: patch review -> resolved
2019-09-19 15:51:48python-devsetpull_requests: + pull_request15872
2019-09-19 15:48:29python-devsetkeywords: + patch
stage: patch review
pull_requests: + pull_request15871
2019-09-19 15:45:01tebekasetmessages: + msg352801
2019-09-19 14:33:41eric.smithsetmessages: + msg352795
2019-09-19 14:31:08serhiy.storchakasetmessages: + msg352794
2019-09-19 12:12:56eric.smithsetmessages: + msg352787
2019-09-19 10:18:39serhiy.storchakasetmessages: + msg352785
2019-09-19 10:04:27eric.smithsetmessages: + msg352784
2019-09-19 09:02:13serhiy.storchakasetmessages: + msg352775
2019-09-19 08:16:20eric.smithsetnosy: + eric.smith
messages: + msg352773
2019-09-19 07:26:14serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg352772
2019-09-19 06:03:27xtreaksetnosy: + pitrou
2019-09-19 06:01:05tebekacreate