import os

class WaterButlerPathPart:
    DECODE = lambda x: x
    ENCODE = lambda x: x

    def __init__(self, part, _id=None):
        self._id = _id
        self._part = part

    @property
    def identifier(self):
        return self._id

    @property
    def value(self):
        return self.__class__.DECODE(self._part)

    @property
    def raw(self):
        return self._part

    def __repr__(self):
        return '{}({!r})'.format(self.__class__.__name__, self._part)


class WaterButlerPath:
    """
    A standardized and validated immutable WaterButler path.
    """

    PART_CLASS = WaterButlerPathPart

    @classmethod
    def generic_path_validation(cls, path):
        """Validates a WaterButler specific path, e.g. /folder/file.txt, /folder/
        :param str path: WaterButler path
        """
        if not path:
            raise exceptions.InvalidPathError('Must specify path')
        if not path.startswith('/'):
            raise exceptions.InvalidPathError('Invalid path \'{}\' specified'.format(path))
        if '//' in path:
            raise exceptions.InvalidPathError('Invalid path \'{}\' specified'.format(path))
        # Do not allow path manipulation via shortcuts, e.g. '..'
        absolute_path = os.path.abspath(path)
        if not path == '/' and path.endswith('/'):
            absolute_path += '/'
        if not path == absolute_path:
            raise exceptions.InvalidPathError('Invalid path \'{}\' specified'.format(absolute_path))

    @classmethod
    def from_parts(cls, parts, folder=False):
        _ids, _parts = [], ['']
        for part in parts:
            _ids.append(part.identifier)
            _parts.append(part.raw)

        return cls('/'.join(_parts), _ids=_ids, folder=folder)

    def __init__(self, path, _ids=(), prepend=None, folder=None):
        self._generic_path_validation(path)

        self._orig_path = path
        path = path.strip('/').split('/')

        _ids = [None] * len(_ids) - len(path) + _ids

        self._parts = [
            self.PART_CLASS(part, _id)
            for part, _id
            in zip(path, _ids)
        ]

        if folder is not None:
            self.is_folder = bool(folder)
        else:
            self.is_folder = self.path.endswith('/')

    @property
    def parts(self):
        return self._parts

    @property
    def name(self):
        return self._parts[-1].value

    @property
    def identifier(self):
        return self._parts[-1].identifier

    @property
    def path(self):
        return '/'.join([x.value for x in self.parts])

    @property
    def parent(self):
        return self.__class__.from_parts(self.parts[:-1])

    def _generic_path_validation(self, path):
        pass

    def __str__(self):
        return '/'.join([''] + [x.raw for x in self.parts]) + ('/' if self.is_folder else '')

    def __repr__(self):
        return '{}({!r})'.format(self.__class__.__name__, self._orig_path)

@@ -1828,16 +1828,25 @@
   self._
+_class__.
 generic_
@@ -1899,24 +1899,25 @@
  = path%0A
+%0A
         
 path = p
@@ -1912,20 +1912,137 @@
     
-path = path.
+self._prepend = prepend%0A%0A        if prepend:%0A            self._prepend_parts = %5Bself.PART_CLASS(part, None) for part in prepend.r
 stri
@@ -2054,25 +2054,25 @@
 ).split('/')
-%0A
+%5D
 %0A        _id
@@ -2072,52 +2072,50 @@
     
-_ids = %5BNone%5D * len(_ids) - len(path) + _ids
+else:%0A            self._prepend_parts = %5B%5D
 %0A%0A  
@@ -2191,25 +2191,28 @@
     for 
-part, _id
+_id, part in
 %0A       
@@ -2221,24 +2221,62 @@
    i
-n zip(path, _ids
+tertools.zip_longest(_ids, path.rstrip('/').split('/')
 )%0A  
@@ -2324,32 +2324,33 @@
            self.
+_
 is_folder = bool
@@ -2385,24 +2385,25 @@
        self.
+_
 is_folder = 
@@ -2407,16 +2407,22 @@
  = self.
+_orig_
 path.end
@@ -2429,24 +2429,338 @@
 swith('/')%0A%0A
+        if self.is_dir and not self._orig_path.endswith('/'):%0A            self._orig_path += '/'%0A%0A    @property%0A    def is_root(self):%0A        return len(self._parts) == 1%0A%0A    @property%0A    def is_dir(self):%0A        return self._is_folder%0A%0A    @property%0A    def is_file(self):%0A        return not self._is_folder%0A%0A
     @propert

Clean up init logic and add some helper functions