Skip to content

Unicode arguments in str.format() #51549

Closed

Description

@doerwalter
BPO 7300
Nosy @doerwalter, @birkenfeld, @vstinner, @ericvsmith, @ezio-melotti, @florentx, @cjerdonek, @s0undt3ch
Files
  • issue7300-trunk.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/ericvsmith'
    closed_at = <Date 2013-03-28.10:33:27.009>
    created_at = <Date 2009-11-10.13:57:33.810>
    labels = ['interpreter-core', 'type-bug']
    title = 'Unicode arguments in str.format()'
    updated_at = <Date 2013-03-28.10:33:27.006>
    user = 'https://github.com/doerwalter'

    bugs.python.org fields:

    activity = <Date 2013-03-28.10:33:27.006>
    actor = 'georg.brandl'
    assignee = 'eric.smith'
    closed = True
    closed_date = <Date 2013-03-28.10:33:27.009>
    closer = 'georg.brandl'
    components = ['Interpreter Core']
    creation = <Date 2009-11-10.13:57:33.810>
    creator = 'doerwalter'
    dependencies = []
    files = ['16514']
    hgrepos = []
    issue_num = 7300
    keywords = ['patch']
    message_count = 10.0
    messages = ['95114', '100769', '100770', '100771', '100861', '178596', '178599', '178617', '178618', '185426']
    nosy_count = 10.0
    nosy_names = ['doerwalter', 'georg.brandl', 'vstinner', 'eric.smith', 'ezio.melotti', 'pablomouzo', 'flox', 'chris.jerdonek', 'gkcn', 's0undt3ch']
    pr_nums = []
    priority = 'high'
    resolution = 'wont fix'
    stage = 'test needed'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue7300'
    versions = ['Python 2.7']

    Activity

    1. doerwalter commented on Nov 10, 2009

      @doerwalter
      ContributorAuthor

      str.format() doesn't handle unicode arguments:

      Python 2.6.4 (r264:75706, Oct 27 2009, 15:18:04) 
      [GCC 4.2.1 (Apple Inc. build 5646)] on darwin
      Type "help", "copyright", "credits" or "license" for more information.
      >>> '{0}'.format(u'\u3042')
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      UnicodeEncodeError: 'ascii' codec can't encode character u'\u3042' in
      position 0: ordinal not in range(128)

      Unicode arguments should be treated in the same way as the % operator
      does it: by promoting the format string to unicode:

      >>> '%s' % u'\u3042'
      u'\u3042'
    2. added
      interpreter-core(Objects, Python, Grammar, and Parser dirs)
      type-bugAn unexpected behavior, bug, or error
      on Nov 10, 2009
    3. vstinner commented on Mar 9, 2010

      @vstinner
      Member

      PyString_Format() uses a "goto unicode;" if a '%c' or '%s' argument is unicode. The unicode label converts the partial formatted result (byte string) to unicode, and use PyUnicode_Format() to finish to formatting.

      I don't think that you can apply the same algorithm here (converts the partial result to unicode) because it requires to rewrite the format string: arguments can be used twice or more, and used in any order.

      Example: "{0} {1}".format("bytes", u"unicode") => switch to unicode occurs at result="bytes ", format=" {1}", arguments=(u"unicode"). Converts "bytes " to unicode is easy, but the format have to be rewritten in " {0}" or something else.

      Call trace of str.format(): do_string_format() -> build_string() -> output_markup() -> render_field(). The argument type is proceed in render_field().

    4. vstinner commented on Mar 9, 2010

      @vstinner
      Member

      *Draft* patch fixing the issue: render_field() raises an error if the argument is an unicode argument, string_format() catchs this error and converts self to unicode and call unicode.format(*args, **kw).

      Pseudo-code:

      try:
      # self.format() raises an error if any argument is
      # an unicode string)
      return self.format(*args,**kw)
      except UnicodeError:
      unicode = self.decode(default_encoding)
      return unicode.format(*args, **kw)

      The patch changes the result type of '{}'.format(u'ascii'): it was str and it becomes unicode. The new behaviour is consistent with "%s" % u"ascii" => u"ascii" (unicode).

      I'm not sure that catching *any* unicode error is a good idea. I think that it would be better to use a new exception type dedicated to this issue, but it looks complex to define a new exception. I will may do it for the next patch version ;-)

    5. vstinner commented on Mar 9, 2010

      @vstinner
      Member

      My patch converts the format string to unicode using the default encoding. It's inconsistent with str%args: str%args converts str to unicode using the ASCII charset (if a least one argument is an unicode string), not the default encoding.

      >>> "\xff%s" % u'\xe9'
      ...
      UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 0: ordinal not in range(128)
    6. ericvsmith commented on Mar 11, 2010

      @ericvsmith
      Member

      I'm not sure I'm wild about doing the work twice, once as string and once as unicode if need be. But I'll consider it, especially since this is only a 2.7 issue.

      There could be side effects of evaluating the replacement strings, but I'm not sure it's worth worrying about. Attribute (or index) access having side effects isn't something I think we need to cater to.

    7. s0undt3ch commented on Dec 30, 2012

      s0undt3chmannequin
      Mannequin

      This is not a 2.7 issue only:

      >>> import sys
      >>> sys.version_info
      (2, 6, 5, 'final', 0
      >>> 'Foo {0}'.format(u'bár')
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in position 1: ordinal not in range(128)
      >>>
    8. ezio-melotti commented on Dec 30, 2012

      @ezio-melotti
      Member

      2.6 only gets security fixes.

      My patch converts the format string to unicode using the default
      encoding. It's inconsistent with str%args: str%args converts str to
      unicode using the ASCII charset (if a least one argument is an unicode
      string), not the default encoding.

      I think it's better to be consistent and use ASCII.

    9. vstinner commented on Dec 30, 2012

      @vstinner
      Member

      Another option is to decide that this issue will *not* be fixed in Python 2, and Python 3 *is* the good solution if you have this issue.

      Doing the work twice can cause new problems, formatting an argument twice may return two different values :-( It may have an impact on performances and may introduce regressions.

      Oh by the way, it's trivial to workaround this issue in Python 2: just use a Unicode format string. For example, replace '{0}'.format(u'\u3042') with u'{0}'.format(u'\u3042').

      I hate implicit conversion from bytes to Unicode in Python 2, it's maybe better to not add a new special case?

    10. ericvsmith commented on Dec 30, 2012

      @ericvsmith
      Member

      I agree that we should close this as "won't fix" in 2.7.

    11. birkenfeld commented on Mar 28, 2013

      @birkenfeld
      Member

      Agreed with Eric.

    12. transferred this issue fromon Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions