Skip to content

Create a bytes version of os.environ and getenvb() #52849

Closed

Description

@vstinner
BPO 8603
Nosy @malemburg, @loewis, @gpshead, @pitrou, @vstinner, @ezio-melotti
Files
  • os_environb-3.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 = None
    closed_at = <Date 2010-09-10.22:19:03.024>
    created_at = <Date 2010-05-03.08:56:24.666>
    labels = ['interpreter-core', 'type-feature', 'library', 'expert-unicode']
    title = 'Create a bytes version of os.environ and getenvb()'
    updated_at = <Date 2010-09-10.22:19:03.023>
    user = 'https://github.com/vstinner'

    bugs.python.org fields:

    activity = <Date 2010-09-10.22:19:03.023>
    actor = 'vstinner'
    assignee = 'none'
    closed = True
    closed_date = <Date 2010-09-10.22:19:03.024>
    closer = 'vstinner'
    components = ['Interpreter Core', 'Library (Lib)', 'Unicode']
    creation = <Date 2010-05-03.08:56:24.666>
    creator = 'vstinner'
    dependencies = []
    files = ['17236']
    hgrepos = []
    issue_num = 8603
    keywords = ['patch']
    message_count = 38.0
    messages = ['104825', '104827', '104838', '104839', '104871', '104877', '104878', '104879', '104880', '104883', '104884', '104886', '104889', '104894', '104895', '104897', '104898', '104900', '104901', '104913', '104920', '104987', '105003', '105006', '105080', '105115', '105116', '105124', '105131', '105142', '105143', '105167', '111878', '111880', '111897', '111972', '111976', '116050']
    nosy_count = 7.0
    nosy_names = ['lemburg', 'loewis', 'gregory.p.smith', 'pitrou', 'vstinner', 'ezio.melotti', 'Arfrever']
    pr_nums = []
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue8603'
    versions = ['Python 3.2']

    Activity

    1. vstinner commented on May 3, 2010

      @vstinner
      MemberAuthor

      As discussed in issue bpo-8514, I propose a bytes version of os.envionb which would be synchronized with os.environ (which is possible thanks to surrogateescape error handler).

      I also propose a os.getenvb()->bytes function.

      I don't know yet if it's a good idea of not, but my patch accepts both bytes and str for os.environ(b).get() and os.getenv(b)().

      antoine> In posixmodule.c, (...) if memory allocation of the bytes
      antoine> object fails, we should error out.

      I would require to change also the Windows version and the code specific to OS/2. Ok to do that, but after closing this issue ;-) I don't want to change to much things at the same time.

    2. added
      interpreter-core(Objects, Python, Grammar, and Parser dirs)
      stdlibStandard Library Python modules in the Lib/ directory
      on May 3, 2010
    3. vstinner commented on May 3, 2010

      @vstinner
      MemberAuthor

      The patch creates also fsencode()/fsdecode() functions proposed in bpo-8514: I can rename them to use protected name (eg. "_encodeenv" and "_decodeenv") until we decided for these functions.

    4. malemburg commented on May 3, 2010

      @malemburg
      Member

      A view comments on the patch:

      + def __init__(self, data, encodekey, decodekey, encodevalue, decodevalue, putenv, unsetenv):

      As general guideline: When adding new parameter, please add them to the
      end of the parameter list and preferably with a default argument in order
      to not break the API.

      Doesn't matter much in this case, since _Environ is only used internally,
      but it's still good practice.

      --

      +data = {}
      +for key, value in environ.items():
      + data[_keymap(key)] = fsencode(value)

      Please put such init code into a function or make sure that the global
      module space is not polluted with temporary variables such as data,
      key, value.

      --

      + # bytes environ
      + environb = _Environ(data, _keymap, fsencode, fsencode, fsencode, _putenv, _unsetenv)

      This looks wrong even though it will work, but that's only a
      side-effect of how fsencode is coded and that's not how the
      stdlib should be coded (see below).

      --

      + def fsencode(value):
      + """
      + unicode to file system
      + """
      + if isinstance(value, bytes):
      + return value
      + else:
      + return value.encode(sys.getfilesystemencoding(), 'surrogateescape')

      The function should not accept bytes as input or at least
      document this pass-through behavior, leaving the user to decide
      whether that's wanted or not.

      --

      The patch is also missing code which keeps the two dictionaries in
      sync. If os.environ changes, os.environb would have to change as
      well.

    5. vstinner commented on May 3, 2010

      @vstinner
      MemberAuthor

      The patch is also missing code which keeps the two dictionaries in
      sync. If os.environ changes, os.environb would have to change as
      well.

      No, it doesn't :-) os.environ and os.environb are synchronized and there is a test for this! ;-)

      I will see later for your other comments.

    6. loewis commented on May 3, 2010

      loewismannequin
      Mannequin

      Can somebody please explain what problem is being solved with this patch?

    7. malemburg commented on May 3, 2010

      @malemburg
      Member

      Martin v. Löwis wrote:

      Martin v. Löwis <[email protected]> added the comment:

      Can somebody please explain what problem is being solved with this patch?

      The way os.environ is currently set up on Unix breaks applications
      using the environment to pass data to helper applications.

      Please see the discussion on http://bugs.python.org/issue8514
      for details.

    8. loewis commented on May 3, 2010

      loewismannequin
      Mannequin

      Please see the discussion on http://bugs.python.org/issue8514
      for details.

      I can't see any report of actual breakage in that report, only claims of
      potential breakage (with no supporting examples)

    9. vstinner commented on May 3, 2010

      @vstinner
      MemberAuthor

      An issue was opened 2 years ago: "It was brought up in a discussion of sending non-ASCii data to a CGI-WSGI script where the data would be transferred via os.environ." => bpo-4006 (closed as "wont fix").

    10. loewis commented on May 3, 2010

      loewismannequin
      Mannequin

      An issue was opened 2 years ago: "It was brought up in a discussion
      of sending non-ASCii data to a CGI-WSGI script where the data would
      be transferred via os.environ." => bpo-4006 (closed as "wont fix").

      Fortunately, that issue could now be reconsidered as "fixed"; the
      example in the report (msg74118) now works correctly - thanks to PEP-383. So I still fail to see the problem.

    11. malemburg commented on May 3, 2010

      @malemburg
      Member

      Martin v. Löwis wrote:

      Martin v. Löwis <[email protected]> added the comment:

      > Please see the discussion on http://bugs.python.org/issue8514
      > for details.

      I can't see any report of actual breakage in that report, only claims of
      potential breakage (with no supporting examples)

      Set your CODESET to ASCII and watch the surrogate escaping
      begin... seriously, Martin, if you've ever worked with CGI
      or WSGI or FastCGI or SCGI or any of the many other protocols
      that use the OS environment for passing data between processes,
      it doesn't take much imagination to come up with examples
      that fail left and right.

      Here's one (RFC 3875, sections 4.1.7 and 4.1.5):

      LANG = 'en_US.utf8'
      CONTENT_TYPE = 'application/x-www-form-urlencoded'
      QUERY_STRING = 'type=example&name=Löwis'
      PATH_INFO = '/home/löwis/bin/mycgi.py'

      (HTML uses Latin-1 as default encoding and so do many of the
      protocols invented for it !)

      The file system encoding simply doesn't relate to the OS
      environment at all - it's just a collection of name=value mappings
      with no explicit encoding information. It may be a good guess,
      but that's it.

    12. loewis commented on May 3, 2010

      loewismannequin
      Mannequin

      Set your CODESET to ASCII and watch the surrogate escaping
      begin... seriously, Martin, if you've ever worked with CGI
      or WSGI or FastCGI or SCGI or any of the many other protocols
      that use the OS environment for passing data between processes,
      it doesn't take much imagination to come up with examples
      that fail left and right.

      Here's one (RFC 3875, sections 4.1.7 and 4.1.5):

      LANG = 'en_US.utf8'
      CONTENT_TYPE = 'application/x-www-form-urlencoded'
      QUERY_STRING = 'type=example&name=Löwis'
      PATH_INFO = '/home/löwis/bin/mycgi.py'

      I still don't see a *failure* here. AFAICT, it all works correctly.
      In particular, I fail to see the advantage of using bytes over using
      escaped strings, in terms of correctness. I'm even skeptical that there
      is an advantage in terms of usability (and if there is, I'd like to see
      a demonstration of that, as well).

    13. loewis commented on May 3, 2010

      loewismannequin
      Mannequin

      Here's one (RFC 3875, sections 4.1.7 and 4.1.5):

      LANG = 'en_US.utf8'
      CONTENT_TYPE = 'application/x-www-form-urlencoded'
      QUERY_STRING = 'type=example&name=Löwis'
      PATH_INFO = '/home/löwis/bin/mycgi.py'

      (HTML uses Latin-1 as default encoding and so do many of the
      protocols invented for it !)

      BTW, I think you are misinterpreting the RFC. It doesn't actually say
      that QUERY_STRING is Latin-1 encoded, but instead, it says

      "the details of the parsing, reserved characters and support for non
      US-ASCII characters depends on the context"

      Latin-1 is only given as a possible example. Apache passes the URL from
      the HTTP request unescaped; browsers will likely CGI-escape it. So most
      likely, it will be

      QUERY_STRING = 'type=example&name=L%F6wis'
      or
      QUERY_STRING = 'type=example&name=L%C3%B6wis'

      IMO, applications are much better off to consider QUERY_STRING as a
      character string.

    14. 16 remaining items

    15. vstinner commented on May 6, 2010

      @vstinner
      MemberAuthor

      Aaaah, *posix*.environ, not *os*.environ, ok. I will fix posix documentation. I didn't knew this dictionary :-)

    16. vstinner commented on May 6, 2010

      @vstinner
      MemberAuthor

      Patch version 3:

      • update posix documentation
      • improve os.environ and os.getenv() documentation: specify the type and document the encoding/error handler, add a link to environb/getenvb
      • os.environ and os.environb now check the argument types (raise a better error), on Windows and Unix. Before my patch, os.environ[b'key']=b'value' sets the variable "b'key'" to "b'value'" :-(
      • restore os.environb in TestEnviron.tearDown() (test_os)
      • fix patch on posixmodule.c indentation
      • fix a regression introduced by my patch: set PyErr_NoMemory() on error (newstr==NULL in posixmodule.c)
      • rename keymap to encodekey
    17. vstinner commented on May 6, 2010

      @vstinner
      MemberAuthor

      Oh no, I forgot to remove the annotations from getenv() and getenvb() in os.py. I only removed them from the documentation.

    18. vstinner commented on May 6, 2010

      @vstinner
      MemberAuthor

      Commited as r80885 (py3k), blocked in 3.1 (r80886).

      Thank you Martin and Marc for your great help and all your reviews ;-)

    19. ezio-melotti commented on Jul 29, 2010

      @ezio-melotti
      Member

      FWIW os.environb is missing from os.__all__.

    20. ezio-melotti commented on Jul 29, 2010

      @ezio-melotti
      Member

      A quick search0 also shows that environ.data is used by several projects. Changing it from str to bytes will most likely break these programs, so I'm not sure it's a good idea.
      Also, if I understand correctly, on Windows os.environ.data still contains str, so it's inconsistent with other systems.
      IMHO os.environ.data should contain str, whereas os.environb.data bytes. If they must share the same data I would prefer them to be both str.

    21. malemburg commented on Jul 29, 2010

      @malemburg
      Member

      Ezio Melotti wrote:

      Ezio Melotti <[email protected]> added the comment:

      A quick search0 also shows that environ.data is used by several projects. Changing it from str to bytes will most likely break these programs, so I'm not sure it's a good idea.
      Also, if I understand correctly, on Windows os.environ.data still contains str, so it's inconsistent with other systems.
      IMHO os.environ.data should contain str, whereas os.environb.data bytes. If they must share the same data I would prefer them to be both str.

      Direct use of os.environ.data is not permitted as it is not a documented
      feature, so I wouldn't worry about this.

      The few uses you found are easy to fix.

    22. vstinner commented on Jul 29, 2010

      @vstinner
      MemberAuthor

      Le jeudi 29 juillet 2010 03:08:27, Ezio Melotti a écrit :

      A quick search[0] also shows that environ.data is used by several projects.

      _Environ is a wrapper on data: call putenv() when a value is changed and
      unputenv() when a value is removed. Since os.environb, it does also convert
      key and value types. So .data should not be used directly.

      It looks like a common usage of .data is to get get to "a real dict" (and not
      "an user dict"). I think that os.environ.data or os.environ.data.copy() can be
      replaced by dict(os.environ) is that case.

      Changing it from str to bytes will most likely break these programs,

      I'm not sure of that. It looks (in the search result) that os.environ.data is
      just stored and passed to a function, but not used as a dictionary to get a
      value (well, I'm not sure because it's hard to say with just 3 lines of the
      program). In that case, it doesn't matter if the dictionary contains byte or
      unicode strings.

      Eg. zope2instance:

        old_env = os.environ.data.copy()
        ...
        os.environ.data = old_env

      It does still work with bytes.

      so I'm not sure it's a good idea

      Yes, it's not a good idea to use .data :-) This attribute should be protected,
      not public (even if it is not documented).

      IMHO os.environ.data should contain str, whereas os.environb.data
      bytes.

      data is shared between os.environ and os.environb, because data is the
      solution to synchronize both dictionaries.

      If they must share the same data I would prefer them to be both
      str.

      No, data should use the native type: bytes on UNIX and BSD, str on Windows.

      --

      If you still consider that the change on .data as a bug, I think that the fix
      is to remove .data (mark it as protected: environ.data => environ._data).

    23. vstinner commented on Jul 29, 2010

      @vstinner
      MemberAuthor

      Le jeudi 29 juillet 2010 03:00:03, Ezio Melotti a écrit :

      FWIW os.environb is missing from os.__all__.

      Fixed by r83237

    24. vstinner commented on Sep 10, 2010

      @vstinner
      MemberAuthor

      If you still consider that the change on .data as a bug,
      I think that the fix is to remove .data (mark it as
      protected: environ.data => environ._data).

      r84690 marks os.environ.data as protected. Close this issue again.

    25. 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

    No one assigned

      Labels

      interpreter-core(Objects, Python, Grammar, and Parser dirs)stdlibStandard Library Python modules in the Lib/ directorytopic-unicodetype-featureA feature request or enhancement

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions