3

Without violating the guideline that a constructor should do work, I need to determine if the provided string (destination_directory) is a valid path before assigning it in the constructor.

It doesn't have to exist, but the provide string must be a valid one, i.e. no invalid symbols, or illegal characters. My project will run on Windows only, not Linux.

I looked at this page, but the answers seem to try and open the directory to test if the provided string is valid.

I also tried os.path.isabs(path)but it doesn't provide the results I require. For example, it says that T:\\\\Pictures is a absolute path, while that may be true, the \\\\ should mean the path is invalid.

Is there a clean, perhaps one line way of achieving what I want?

def __init__(self, destination_directory: str)
    self._validate_path(path=destination_directory)
    self.destination_directory = destination_directory

def _validate_path(self, path)
    # code to validate path should go here.

2 Answers2

1

We now a few things about a path, it contains at least a drive letter and subdirectories.

We also have rules about what symbols are not allowed in directories. We also know that a drive letter contains a single character.

Instead of allowing users of our class to pass in a full path, we break it down and only allow valid strings for directories names and one letter for the drive. When everything is validated, we can use the os module to build our path.

Here is how I would structure my Folder class:

class Folder:

    def __init__(self, *subdirectories, root_drive):
        self._validate_drive_letter(letter = root_drive)
        self._validate_path(path=subdirectories)

        self._root_drive = root_drive
        self._subdirectories = subdirectories

    def _validate_drive_letter(self, letter):
        if not letter or len(letter) > 2 or not letter.isalpha():
            raise ValueError("Drive letter is invalid")

    def _validate_path(self, path):
        self._forbidden_characters = ["<", ">", ":", "/", '"', "|", "?", "*", '\\']
        for character in path:
            for item in character:
                if item in self._forbidden_characters:
                    raise ValueError("Directory cannot contain invalid characters")

    def construct_full_path(self) -> str:
        # use the os module and constructor parameters to build a valid path

    def __str__(self) -> str:
        return f"Drive Letter: {self._root_drive} Subdirectories: {self._subdirectories}"

Main:

def main():

    try:

        portable_drive = Folder("Pictures", "Landscape", root_drive="R") # Valid
        # Using the construct_full_path() function, the returned string would be:
        # R:\Pictures\Landscape
        # Notice the user doesn't provide the : or the \, the class will do it.

        vacation_pictures = Folder("Vac??tion", root_drive="T") # Will raise ValueError
        # If we fix the error and call construct_full_path() we will get T:\Vacation 

    except ValueError as error:
        print(error)
    else:
        print(portable_drive)
        print(vacation_pictures)


if __name__ == "__main__":
    main()

It may not be the best approach, but it works. I know a nested for loop is bad, but I don't see any other way to validate the individual characters of a string.

0

A regex solution:

import re

windows_path_regex = re.compile(r"""
\A
(?:(?:[a-z]:|\\\\[a-z0-9_.$\●-]+\\[a-z0-9_.$\●-]+)\\|  # Drive
   \\?[^\\/:*?"<>|\r\n]+\\?)                           # Relative path
(?:[^\\/:*?"<>|\r\n]+\\)*                              # Folder
[^\\/:*?"<>|\r\n]*                                     # File
\Z
""", re.VERBOSE|re.I)
d = windows_path_regex .match(r"\test\txt.txt")
print(bool(d))

Note that\ is a valid path but / is not.

I used 8.18. Validate Windows Paths as a reference.

  • The [docs](https://docs.python.org/3.7/library/os.path.html#os.path.isdir) state that `isdir()` will test if the path is an existing directory. The directory in my example doesn't exist(yet). I want to ensure the string provide is valid for a directory name, it doesn't matter if it exists or not. –  Apr 15 '19 at 14:44
  • No problem. I've written my own answer as well, won't mind getting feedback. :D. –  Apr 15 '19 at 14:48
  • 1
    @nsonline Try that solution – Error - Syntactical Remorse Apr 15 '19 at 15:38