Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Changing Directory with a Python Context Manager
Thomas Eckert
Thomas Eckert

Posted on

     

Changing Directory with a Python Context Manager

Originally published onthomaseckert.dev

In my latest project, I've added Tailwind and Vue to a Flask app. This requires an additional build step to compile each usingnpm during deployment. Given that Python is already present on the server by the time the build step occurs, I decided to write the build script in Python.

Those who have written build scripts before may be familiar with the pattern of changing into a directory, executing commands, then returning to the original directory to start the next set of commands. That was exactly what I needed to do here:

  1. Change directory to./tailwind
    1. Executenpm install to install dependencies
    2. Executenpm run build to compile the Tailwind CSS
  2. Change directory..
  3. Change directory./vue
    1. Executenpm install to install dependencies
    2. Executenpm run build to compile the Vue application

This seemed like a perfect fit for a Python context manager. Context managers allow for the instatiation of a context using thewith keyword. The context is disposed when the code is dedented. Using a context manager to change directories here would eliminate the relative path directory change in step 2, once the first step is completed, the directory would be automatically reset to what it was before the context was initiated.

By writing the right context managerset_directory, I could implement the build script as

frompathlibimportPathwithset_directory(Path("./tailwind")):run_npm_install()run_npm_build()withset_directory(Path("./vue")):run_npm_install()run_npm_build()
Enter fullscreen modeExit fullscreen mode

and I thought that was pretty slick!

Context managers can be written as classes or functions. Given the relative simplicity of this context, I opted to use a function. A context manager function must be decorated with@contextmanager which is imported fromcontextlib. It should have atry block with ayield and afinally block. When the context is instantiated using thewith keyword, thetry block is run. When the indented code block is left, thefinally block is run. As an example,

fromcontextlibimportcontextmanager@contextmanagerdeffriendly_context():try:print("Hello! Welcome to the context!")yieldfinally:print("Bye now. Thank you for visiting the context. Come again soon.")withfriendly_context():print("Oh thank you, it is so nice to be in the context.")
Enter fullscreen modeExit fullscreen mode

when executed will print

Hello! Welcome to the context!Oh thank you, it is so nice to be in the context.Bye now. Thank you for visiting the context. Come again soon.
Enter fullscreen modeExit fullscreen mode

To write my directory changing context manager, I needed to save the original path to a variable, change it in thetry block to whatever was passed in to the function, then change to the original path in thefinally block.

fromcontextlibimportcontextmanagerfrompathlibimportPathimportos@contextmanagerdefset_directory(path:Path):"""Sets the cwd within the context    Args:        path (Path): The path to the cwd    Yields:        None    """origin=Path().absolute()try:os.chdir(path)yieldfinally:os.chdir(origin)
Enter fullscreen modeExit fullscreen mode

And it works like a charm! Let me know if you found a cool use for context managers or would have solved this problem a different way.

Top comments(3)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
waylonwalker profile image
Waylon Walker
👋 Hey there, I am Waylon WalkerI am a Husband, Father of two beautiful children, Senior Python Developer currently working in the Data Engineering platform space. I am a continuous learner, and sha
  • Location
    Peoria, Illinois
  • Work
    Python Dev
  • Joined

Great use case for a context manager. Outside of opening files context managers are far too underutilized.

There is also an alternativeclass based syntax that you may run into on occasion.

classset_directory(object):"""Sets the cwd within the context      Args:          path (Path): The path to the cwd      """def__init__(self,path:Path):self.path=pathself.origin=Path().absolute()def__enter__(self):os.chdir(self.path)def__exit__(self):os.chdir(self.origin)
Enter fullscreen modeExit fullscreen mode
CollapseExpand
 
teckert profile image
Thomas Eckert
Proficient in Microsoft, word.
  • Location
    Seattle
  • Education
    Masters in Physics
  • Work
    Software Engineer at Microsoft
  • Joined

Great point! Thank you for providing a full class example. I'm sure people will find it useful to reference!

CollapseExpand
 
jmartens profile image
Jonathan Martens
  • Joined

Is there any specific reason for using Path().absolute( ) over os.getcwd(). It would save you a dependency to the pathlib's Path class.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Proficient in Microsoft, word.
  • Location
    Seattle
  • Education
    Masters in Physics
  • Work
    Software Engineer at Microsoft
  • Joined

More fromThomas Eckert

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp