Skip to content

Instantly share code, notes, and snippets.

@deric4
Last active February 10, 2020 23:54
Show Gist options
  • Save deric4/5e312be31dafabae885d3ba8b2631932 to your computer and use it in GitHub Desktop.
Save deric4/5e312be31dafabae885d3ba8b2631932 to your computer and use it in GitHub Desktop.
pdx-python-pirates:contextmanager:new_log_file()

Initial program

from contextlib import contextmanager
from shutil import copy
from os import getcwd
@contextmanager
def new_log_file(name):
    try:
        logname = name
        f = open(logname, "w")
        f.write("This is a header.\n")
        yield f
    finally:
        f.write("\nThis is a footer.")
with new_log_file("test1.txt") as file:
    file.write("This is the body.")
with new_log_file("test2.txt") as file:
    shutil.copy(file, os.getcwd) #problem line

Step 1

Small change I made from the code posted in slack. https://pythonpirates.slack.com/archives/CCJM4HMKJ/p1581034519012900

 with new_log_file("test2.txt") as file:
-    copy(file, os.getcwd) #problem line
+    copy(file, getcwd) #problem line
    "assuming you made the following change to get the error posted in slack"

ERROR

(venv) miguel :: ~/test Β» python test2.py 
Traceback (most recent call last):
  File "test2.py", line 19, in <module>
    copy(file, getcwd) #problem line
  File "/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/shutil.py", line 246, in copy
    if os.path.isdir(dst):
  File "/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/genericpath.py", line 42, in isdir
    st = os.stat(s)
TypeError: stat: path should be string, bytes, os.PathLike or integer, not builtin_function_or_method

This error is caused because os.getcwd is a function which returns a path, so it should be "called", which can be done like this:

 with new_log_file("test2.txt") as file:
-    copy(file, getcwd) #problem line
+    copy(file, getcwd()) #problem line

Step 2

After making the the getcwd() everything should be good right? Wrong lol. we have a very similar problem with the file parmaeter being passed to copy. We can quickly fix that though with by accessing the name attribute on the file object:

with new_log_file("test2.txt") as file:
-    copy(file, getcwd) #problem line
+    copy(file.name, getcwd()) #problem line

ERROR caused by copy(file, getcwd())

Traceback (most recent call last):
 File "test3.py", line 18, in <module>
   copy(file, getcwd()) #problem line
 File "/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/shutil.py", line 247, in copy
   dst = os.path.join(dst, os.path.basename(src))
 File "/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/posixpath.py", line 146, in basename
   p = os.fspath(p)
TypeError: expected str, bytes or os.PathLike object, not _io.TextIOWrapper

Step 3

not sure if this applies to windows, but will def cause a prob on mac and linux

Unfortunately we run into another error after fixing the file.name and getcwd() problem fixed in step-1 and step-2:

Traceback (most recent call last):
 File "test3.py", line 18, in <module>
   copy(file.name, getcwd()) #problem line
 File "/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/shutil.py", line 248, in copy
   copyfile(src, dst, follow_symlinks=follow_symlinks)
 File "/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/shutil.py", line 104, in copyfile
   raise SameFileError("{!r} and {!r} are the same file".format(src, dst))
shutil.SameFileError: 'test2.txt' and '/Users/miguel/test/test2.txt' are the same file

This one is much easier to reason about, we're trying to do the equivalent of:

$ cp test2.txt test2.txt

which gives almost the same error output when trying to copy a file with the same name in the same directory on the CLI:

# current working dir after running script.
$ ls -l 

-rw-r--r--  1 miguel  staff  452 Feb 10 15:37 test.py
-rw-r--r--  1 miguel  staff   53 Feb 10 17:12 test1.txt
-rw-r--r--  1 miguel  staff   36 Feb 10 17:12 test2.txt


$ cp test2.txt test2.txt
cp: test2.txt and test2.txt are identical (not copied).

a quick fix is to just copy test2.txt to a different name: test2-copy.txt

with new_log_file("test2.txt") as file:
-    copy(file, getcwd) #problem line
+    copy(file.name, 'test2-copy.txt') #problem line

Which runs successfully! but we have a bug πŸ›!

Step 4

Even though the script ran, its not super functional. The test2-copy.txt file is empty πŸ™€!

$ ls -l 

-rw-r--r--    1 miguel  staff  452 Feb 10 15:37 test.py
-rw-r--r--    1 miguel  staff   53 Feb 10 17:12 test1.txt
-rw-r--r--    1 miguel  staff   36 Feb 10 17:12 test2.txt
-rw-r--r--    1 miguel  staff    0 Feb 10 17:32 test2-copy.txt

This was the trickiest for me to pin down, but the fix was pretty simple. In our new_log_file() context manager, we need to make sure we

  1. close the file after we're done with it πŸ™ƒ
  2. move the copy statement outside of the context manager
 def new_log_file(name):
         yield f
     finally:
         f.write("\nThis is a footer.")
+        f.close()
 
 ... 
 ...
 ...
 
 with new_log_file("test1.txt") as file:
     file.write("This is the body.")
 
 with new_log_file("test2.txt") as file:
-    copy(file.name, 'test2-copy.txt') #problem line
+    file.write("hello, pdx!") # need something here because of pythons syntax rules. you could just put a "pass" statement
+    
+copy(file.name, 'test2-copy.txt') #problem line

Final Solution

After all the changes we end up with this:

from contextlib import contextmanager
from shutil import copy
from os import getcwd
@contextmanager
def new_log_file(name):
    try:
        logname = name
        f = open(logname, "w")
        f.write("This is a header.\n")
        yield f
    finally:
        f.write("\nThis is a footer.")
        f.close()

with new_log_file("test1.txt") as file:
    file.write("This is the body.")

with new_log_file("test2.txt") as file:
    file.write("hello, pdx!")
    
copy(file.name, 'test2-copy.txt')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment