Here I think we need to drop our perfectionist attitude. When I saw Marc-Andre's proposal my first response was also "but what about threads." But really, os.chdir() is the culprit here, and since it's a syscall we can't fix it. If we can live with that, we can live with the proposed os.workdir(). The docs just need a warning about threads.

Yes, you could create a thread-safe version of this by overriding open() -- and several hundred other functions that refer to pathnames natively. Such a project will inevitably have serious backwards compatibility concerns (any use of pathnames from C extensions will not work right) so it's just not going to be done.

If we don't offer this in the stdlib, users will just implement this themselves, poorly (for example, by not restoring the original when done).

On Tue, Sep 14, 2021 at 2:43 PM Cameron Simpson <> wrote:
On 14Sep2021 21:43, M.-A. Lemburg <> wrote:
>- The context manager is not thread safe. There's no thread safe model
>  for the current work dir. OTOH, scripts usually don't use threads,
>  so not a big deal.

This is the source of my concerns. Though of course it applies to any
process global state. It would need this stated up front in big letters
(as of course does chdir itself). My real concern is that this can leak
into other functions whose use then makes whatever uses them to become
inherently and unrepairably not thread safe.

I know I'm atypical, but I have quite a lot of multithreaded stuff,
including command line code. So while it'd be ok to avoid this context
manager for my own code, I fear library modules, either stdlib or pypi,
quietly using this in their code, making them unuseable in the general
case. Unrepairably unuseable, for the user.

I think what would be more useful is a context manager which worked on a
threading.local which pushed/popped a reference directory path, and had
an open() method which used that (and friends for other pathname based

In my own code I'd write this like (sketch, untested):

    from cs.threads import State as ThreadState

    class RefDir(ThreadState)

      def __init__(self, refpath=None):
        if refpath is None:
          refpath = os.getcwd()
        self.refpath = abspath(refpath)

      def open(self, path, *a, **kw):
        if not isabs(path):
          path = os.path.join(self.refpath, path)
        return open(path, *a, **kw) # calls the builtin open()

      ... listdir, mkdir, etc etc ...

      # on reflection, __enter__ and __exit__ would make the below even
      # more convenient
      def dirpath(newrefpath):
        ''' Push `newrefpath` for the duration of the context manager.
        with self(refpath=newrefpath):
          yield self.refpath

and then use it like this:

    R = RefDir()
    with R.dirpath('/some/new/place') as newpath:
      with"something.txt") as f:
          ... work on /some/new/place/something.txt ...

In the above, cs.threads.State is a threading.lcoal subclass which is
also a context manager whose operation pushes arbitrary attribute
values.  Great for thread safe execution scoped state. Like this

All that said, I wrote pretty much exactly what you describe just the
other week for umask().

Cameron Simpson <>
Python-ideas mailing list --
To unsubscribe send an email to
Message archived at
Code of Conduct:

--Guido van Rossum (
Pronouns: he/him (why is my pronoun here?)