7.1. File Path

7.1.1. Rationale

  • Python works with both relative and absolute path

  • Path separator \ (backslash) is used on Windows

  • Path separator / (slash) is used on *nix operating systems: Linux, macOS, BSD and other POSIX compliant OSes (excluding Windows)

  • In newer Windows versions both \ and / works the same

Absolute path on Windows:

>>> FILE = r'C:\Users\Watney\myfile.txt'

Absolute path on *nix (Linux, macOS, BSD, etc.):

>>> FILE = '/tmp/myfile.txt'

Relative paths works the same on Windows and *nix (Linux, macOS, BSD, etc.):

>>> FILE = 'myfile.txt'
>>> FILE = 'tmp/myfile.txt'
>>> FILE = '../myfile.txt'

7.1.2. Good Engineering Practices

  • Never hardcode paths, use constant as a file name or file path

  • Convention (singular form): FILE, FILENAME, FILEPATH, PATH

  • Convention (plural form): FILES, FILENAMES, FILEPATHS, PATHS

  • Note, that PATH is usually used for other purposes (sys.path or os.getenv('PATH'))

    >>> FILE = 'myfile.txt'
    
    >>> FILES = [
    ...     'myfile.txt',
    ...     'myfile.csv']
    

7.1.3. Raw Strings

  • Always use raw-strings (r"...") for paths

  • Escapes does not matters

    >>> print(r'C:\Users\Admin\file.txt')
    C:\Users\Admin\file.txt
    
    >>> print('C:\\Users\\Admin\\file.txt')
    C:\Users\Admin\file.txt
    
    >>> print('C:\Users\Admin\file.txt')
    Traceback (most recent call last):
    SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \UXXXXXXXX escape
    
  • Problem: \Users

  • after \U... python expects Unicode codepoint in hex i.e. 'U0001F680' which is 🚀 emoticon

  • s is invalid hexadecimal character

  • Only valid characters are 0123456789abcdefABCDEF

    >>> import string
    >>>
    >>>
    >>> print(string.hexdigits)
    0123456789abcdefABCDEF
    

7.1.4. Absolute Path

  • Absolute path on Windows starts with drive letter

  • Absolute path on *nix starts with root / dir

  • Absolute path include all entries in the directories hierarchy

    >>> FILE = r'C:\Users\Watney\myfile.txt'
    
    >>> FILE = r'/tmp/myfile.txt'
    

7.1.5. Relative Path

  • Path is relative to currently running script

  • . - Current directory

  • .. - Parent directory

>>> FILE = r'myfile.txt'
>>> FILE = r'./myfile.txt'
>>> FILE = r'tmp/myfile.txt'
>>> FILE = r'./tmp/myfile.txt'
>>> FILE = r'../myfile.txt'
>>> FILE = r'../tmp/myfile.txt'
>>> FILE = r'../../myfile.txt'
>>> FILE = r'../../tmp/myfile.txt'

7.1.6. Escaping Characters in Path

  • "\ " (backslash space) - escapes space

  • Note that in Python escapes in paths are not required

    >>> FILE = '/tmp/my file.txt'
    
    >>> FILE = r'/tmp/my file.txt'
    
    >>> FILE = r'C:\Users\Admin\myfile.txt'
    >>>
    >>>
    >>> repr(FILE)
    "'C:\\\\Users\\\\Admin\\\\myfile.txt'"
    >>>
    >>> str(FILE)
    'C:\\Users\\Admin\\myfile.txt'
    >>>
    >>> print(repr(FILE))
    'C:\\Users\\Admin\\myfile.txt'
    >>>
    >>> print(FILE)
    C:\Users\Admin\myfile.txt
    

7.1.7. Exception Handling

>>> try:
...     file = open('/tmp/myfile.txt')
... except FileNotFoundError:
...     print('Sorry, file not found')
... except PermissionError:
...     print('Sorry, not permitted')
Sorry, file not found

7.1.8. Create Directories

>>> from pathlib import Path
>>>
>>> Path('/tmp/a').mkdir()
>>> from pathlib import Path
>>>
>>> Path('/tmp/a').mkdir()
Traceback (most recent call last):
FileExistsError: [Errno 17] File exists: '/tmp/a'
>>> from pathlib import Path
>>>
>>> Path('/tmp/a').mkdir(exist_ok=True)
>>> from pathlib import Path
>>>
>>> Path('/tmp/a/b/c').mkdir(parents=True, exist_ok=True)

7.1.9. Touch File

>>> from pathlib import Path
>>>
>>> Path('/tmp/myfile.txt').touch()

7.1.10. Exists and is Directory or File

>>> from pathlib import Path
>>>
>>> Path('/tmp/myfile.txt').exists()
True
>>> from pathlib import Path
>>>
>>> Path('/tmp/myfile.txt').is_dir()
False
>>> from pathlib import Path
>>>
>>> Path('/tmp/myfile.txt').is_file()
True

7.1.11. Delete directory

Works only with empty directories:

>>> from pathlib import Path
>>>
>>> Path('/tmp/a').rmdir()
Traceback (most recent call last):
OSError: [Errno 66] Directory not empty: '/tmp/a'

Remove directories with files:

>>> from shutil import rmtree
>>>
>>> rmtree('/tmp/a', ignore_errors=True)

7.1.12. Current Working Directory

  • Returns an absolute path to current working directory

    >>> from pathlib import Path
    >>>
    >>> # doctest: +SKIP
    ... Path.cwd()
    PosixPath('/home/python/')
    

7.1.13. Convert Relative Path to Absolute

>>> from pathlib import Path
>>>
>>> # doctest: +SKIP
... Path(Path.cwd(), 'myfile.txt')
PosixPath('/home/python/myfile.txt')

7.1.14. Script Path

  • Returns an absolute path to currently running script

    >>> print(__file__)  # doctest: +SKIP
    /home/python/myscript.py
    

7.1.15. Assignments

Code 7.1. Solution
"""
* Assignment: File Path Exception
* Filename: file_path_exception.py
* Complexity: easy
* Lines of code: 6 lines
* Time: 3 min

English:
    1. Using `input()` ask user for a file path
    3. Handle exception for not existing file
    4. Handle exception for not having sufficient permissions

Polish:
    1. Używając `input()` zapytaj użytkownika o ścieżkę do pliku
    3. Obsłuż wyjątek dla nieistniejącego pliku
    4. Obsłuż wyjątek dla braku wystarczających uprawnień

Tests:
    TODO: Doctests
    TODO: Input Stub
"""


# Given
filename = input('Type filename: ')


Code 7.2. Solution
"""
* Assignment: File Path Abspath
* Filename: file_path_abspath.py
* Complexity: easy
* Lines of code: 3 lines
* Time: 5 min

English:
    1. Define `path` with converted `filename` to absolute path
    2. Print if path exists and leads to file or directory
    3. Compare result with "Tests" section (see below)

Polish:
    1. Zdefiniuj `path` z przekonwertowym `filename` do ścieżki bezwzględnej
    2. Wypisz czy ścieżka istnieje i czy prowadzi do pliku czy katalogu
    3. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Tests:
    TODO: Input Stub
    >>> isinstance(result, Path)
    True
    >>> current_directory = Path.cwd()
    >>> str(current_directory) in str(result)
    True
"""


# Given
from pathlib import Path


filename = 'myfile.txt'
path = ...
result = ...