예제 #1
0
 def __getitem__(self, path):
     """Use path-like strings to index `FakeDir` objects."""
     if not isinstance(path, str):
         raise FakedirError("Can only index FakeDir with int or str, "
                            "not {}".format(type(path)))
     paths = path.split('/')
     current = self
     for p in paths:
         for f in current._children:
             if p == f.name:
                 current = f
                 break
         else:
             raise(FakedirError('Path "{}" not found through {}'.format(path, self)))
     return current
예제 #2
0
    def parent(self, other):
        '''
        Setter for the `parent` attribute.

        When a new parent is assigned for an object, this method
        verifies that the other object is `seedir.fakedir.FakeDir`, and that the other
        objects children do not contain a fake item with the same name.

        If those conditions are met, `self` is removed from the children
        of its current parent, and its parent attribute is reassigned.
        Depths are also reset.

        Parameters
        ----------
        other : seedir.fakedir.FakeDir
            Fake directory to become the new `parent` for self.

        Raises
        ------
        FakedirError
            When other is not `seedir.fakedir.FakeDir` or when `self.name` is in
            the child names of other.

        Returns
        -------
        None.

        '''
        if other:
            if not isinstance(other, FakeDir):
                raise FakedirError('other parameter must be instance of FakeDir')
            if self.name in other.get_child_names():
                raise FakedirError('FakeDirs must have unique file/folder names')
            other._children.append(self)
        if self.parent is not None:
            self.parent._children.remove(self)
        self._parent = other
        self.set_depth()
        if isinstance(self, FakeDir):
            self.set_child_depths()
예제 #3
0
def beyond_fakedepth_str(objs, beyond):
    '''
    Generates the text for seedir.FakeDir.seedir() when using the 'beyond'
    parameter and ther are more items than the itemlimit or contents
    beyond the depthlimit.

    Parameters
    ----------
    beyond : str
        Style of beyond string to generate.  Options are:
            - 'ellipsis' ('...')
            - 'content' or 'contents' (the number of files and folders beyond)
            - a string starting with '_' (everything after the leading
              underscore will be returned)
    objs : collection of FakeDir and FakeFile objects, optional
        Objects beyond the limit, used when the 'beyond'
        argeument is 'content' or 'contents'. The default is None.

    Raises
    ------
    FakedirError
        Raised when the 'beyond' argument is not recognized.

    Returns
    -------
    str
        String indicating what lies beyond

    '''
    if beyond == 'ellipsis':
        return '...'
    elif beyond in ['contents', 'content']:
        folders = count_fakedirs(objs)
        files = count_fakefiles(objs)
        return '{} folder(s), {} file(s)'.format(folders, files)
    elif beyond and beyond[0] == '_':
        return beyond[1:]
    else:
        s1 = '"beyond" must be "ellipsis", "content", or '
        s2 = 'a string starting with "_"'
        raise FakedirError(s1 + s2)
예제 #4
0
def populate(fakedir, depth=3, folders=2, files=2, stopchance=.5, seed=None,
             extensions=['txt']):
    '''
    Function for populating `seedir.fakedir.FakeDir` objects with random files and folders.
    Used by `seedir.fakedir.randomdir()`.  Random dictionary names are chosen
    for file and folder names.

    Parameters
    ----------
    fakedir : seedir.fakedir.FakeDir
        Fake directory to populate.
    depth : int, optional
        Maximum depth to create folders and files. The default is `3`.
    folders : int or collection of integers, optional
        Parameter for setting the number of folders per directory.
        The default is `2`.  If `int`, represents the number of folders
        per directory.  If collection of integers, a random value will be
        chosen from the collection each time a directory is popualted.
    files : int or collection of integers, optional, optional
        Same as the folders parameter, but for files.
    stopchance : float between 0 and 1, optional
        Chance that an added folder will not be populated. The default is `.5`.
    seed : int or float, optional
        Random seed. The default is `None`.
    extensions : list-likie, optional
        Collection of extensions to randomly select from for files.  The
        default is `['txt']`.  Leading period can be included or omitted.

    Raises
    ------
    FakedirError
        Issue selecting int from folders or files.

    Returns
    -------
    None, input is modified in place.

    '''
    random.seed(seed)
    if not isinstance(folders, int):
        try:
            fold_num = get_random_int(folders, seed=seed)
        except:
            raise FakedirError('folders must be an int or collection of int')
    else:
        fold_num = folders
    if not isinstance(files, int):
        try:
            file_num = get_random_int(files, seed=seed)
        except:
            raise FakedirError('files must be an int or collection of int')
    else:
        file_num = files
    for i in range(file_num):
        name = random.choice(words) + random.choice(extensions)
        while name in [f.name for f in fakedir._children]:
            name = random.choice(words) + random.choice(extensions)
        fakedir.create_file(name)
    for i in range(fold_num):
        name = random.choice(words)
        while name in [f.name for f in fakedir._children]:
            name = random.choice(words)
        fakedir.create_folder(name)
    for f in fakedir._children:
        if isinstance(f, FakeDir):
            if f.depth <= depth and random.uniform(0, 1) > stopchance:
                if seed is not None:
                    seed += random.random()
                populate(f, depth=depth, folders=folders, files=files,
                         seed=seed, stopchance=stopchance,
                         extensions=extensions)
예제 #5
0
    def trim(self, depthlimit):
        """
        Remove items beyond the `depthlimit`.

        ```
        >>> import seedir as sd
        >>> r = sd.randomdir(seed=456)
        >>> r
        MyFakeDir/
        ├─Vogel.txt
        ├─monkish.txt
        ├─jowly.txt
        ├─scrooge/
        │ ├─light.txt
        │ ├─reliquary.txt
        │ ├─sandal/
        │ ├─paycheck/
        │ │ ├─electrophoresis.txt
        │ │ └─Pyongyang/
        │ └─patrimonial/
        ├─Uganda/
        └─pedantic/
          └─cataclysmic.txt

        >>> r.trim(1)
        >>> r
        MyFakeDir/
        ├─Vogel.txt
        ├─monkish.txt
        ├─jowly.txt
        ├─scrooge/
        ├─Uganda/
        └─pedantic/

        ```


        Parameters
        ----------
        depthlimit : non-negative int
            Files beyond this depth will be cut. The root has depth `0`.

        Raises
        ------
        FakedirError
            `depthlimit` is not a non-negative int

        Returns
        -------
        None.

        """
        depthlimit = int(depthlimit)
        if depthlimit < 0:
            raise FakedirError('depthlimit must be non-negative int')
        depthlimit += self.depth
        def trim_apply(f, depthlimit):
            if depthlimit is not None and f.depth == depthlimit:
                if isinstance(f, FakeDir):
                    f.delete(f.listdir())
        if depthlimit == self.depth:
            self.delete(self.listdir())
        else:
            self.walk_apply(trim_apply, depthlimit=depthlimit)
예제 #6
0
    def seedir(self, style='lines', printout=True, indent=2, uniform=None,
               anystart=None, depthlimit=None, itemlimit=None, beyond=None,
               first=None, sort=False, sort_reverse=False, sort_key=None,
               include_folders=None, exclude_folders=None, include_files=None,
               exclude_files=None, regex=False, slash='/', mask=None,
               formatter=None, **kwargs):
        '''

        Create a folder tree diagram for `self`.  `seedir.fakedir.FakeDir` version of
        `seedir.realdir.seedir()` (see its documentation for examples).

        Parameters
        ----------
        style : 'lines', 'dash', 'arrow', 'spaces', 'plus', or 'emoji', optional
            Style to use. The default is `'lines'`.  A style determines the set
            of characters ("tokens") used to represent the base structure of
            the directory (e.g. which items belong to which folders, when items
            are the last member of a folder, etc.).  The actual tokens being used
            by each style can be viewed with `seedir.printing.get_styleargs()`.
        printout : bool, optional
            Print the folder structure in the console. The default is `True`.  When
            `False`, the folder diagram is returned as a string.
        indent : int (>= 0), optional
            Number of spaces separating items from their parent folder.
            The default is `2`.
        uniform : str or None, optional
            Characters to use for all tokens when creating the tree diagram.
            The default is `None`.  When not `None`, the extend, space, split, and
            final tokens are replaced with `uniform` (the `'spaces'` style is
            essentially `uniform = '  '`).
        anystart : str or None, optional
            Characters to append before any item (i.e. folder or file).  The
            default is `None`.  Specific starts for folders and files can be
            specified (see `**kwargs`).
        depthlimit : int or None, optional
            Limit the depth of folders to traverse.  Folders at the `depthlimit` are
            included, but their contents are not shown (with the exception of the
            beyond parameter being specified).  The default is `None`, which can
            cause exceptionally long runtimes for deep or extensive directories.
        itemlimit : int or None, optional
            Limit the number of items in a directory to show.  Items beyond the
            `itemlimit` can be expressed using the `beyond` parameter.  The files and
            folders left out are determined by the sorting parameters
            (`sort`, `sort_reverse`, `sort_key`).  The default is `None`.
        beyond : str ('ellipsis', 'cotent' or a string starting with an underscore) or None, optional
            String to indicate directory contents beyond the `itemlimit` or the
            `depthlimit`.  The default is `None`.  Options are: `'ellipsis'` (`'...'`),
            `'content'` or `'contents'` (the number of files and folders beyond), or
            a string starting with `'_'` (everything after the leading underscore
            will be returned)
        first : 'files', 'folders', or None, optional
            Sort the directory so that either files or folders appear first.
            The default is `None`.
        sort : bool, optional
            Sort the directory. With no other specifications, the sort will be a
            simple alphabetical sort of the item names, but this can be altered
            with the `first`, `sort_reverse`, and `sort_key parameters`.
            The default is `False`.
        sort_reverse : bool, optional
            Reverse the sorting determined by `sort` or `sort_key`.
            The default is `False`.
        sort_key : function, optional
            Key to use for sorting file or folder names, akin to the `key` parameter
            of the builtin `sorted()` or `list.sort()`. The function should take a
            string as an argument. The default is `None`.
        include_folders, exclude_folders, include_files, exclude_files : str, list-like, or None, optional
            Folder / file names to include or exclude. The default is `None`.
        regex : bool, optional
            Interpret the strings of include/exclude file/folder arguments as
            regular expressions. The default is `False`.
        mask : function, optional
            Function for filtering items.  Each individual item object
            is passed to the mask function.  If `True` is returned, the
            item is kept.  The default is `None`.
        formatter : function, optional
            Function for customizing the tokens used for specific items during
            traversal.

            The formatter function should accept a FakeItem as a single argument,
            and it should return either a dictionary or None. The dictionary
            should have names of seedir tokens as keys ('split', 'extend',
            'space', 'final', 'folderstart', or 'finalstart') and strings
            to use for those tokens as values.  Call `seedir.printing.get_styleargs()`
            for examples.  Though note, not all six tokens need to be specified.

            If None is returned by formatter, the tokens will be set by `style`.

            Note that items exlcuded by the inclusion/exclusion arguments (or the
            `mask`) *will not* be seen by formatter.  Alternatively, any folder tree
            entries created by the `beyond` argument *will* be seen by formatter.
            These items will be of type `str` rather than FakeItem, so formatter
            will need to handle that.

        slash : str, option:
            Slash character to follow folders.  If `'sep'`, uses `os.se`p.  The
            default is `'/'`.
        **kwargs : str
            Specific tokens to use for creating the file tree diagram.  The tokens
            use by each builtin style can be seen with `seedir.printing.get_styleargs()`.
            Valid options are `extend` (characters to show the extension of a directory
            while its children are traversed), `space` (character to provide the
            correct indentation of an item when some of its parent / grandparent
            directories are completely traversed), `split` (characters to show a
            folder or file within a directory, with more items following),
            `final` (characters to show a folder or file within a directory,
            with no more items following), `folderstart` (characters to append
            before any folder), and `filestart` (characters to append beffore any
            file).  The following shows the default tokens for the `'lines'` style:

            >>> import seedir as sd
            >>> sd.get_styleargs('lines')
            {'split': '├─', 'extend': '│ ', 'space': '  ', 'final': '└─', 'folderstart': '', 'filestart': ''}

            All default style tokens are 2 character strings, except for
            `folderstart` and `filestart`.  Style tokens from `**kwargs` are not
            affected by the indent parameter.  The `uniform` and `anystart`
            parameters can be used to affect multiple style tokens.

        Raises
        ------
        SeedirError
            Improperly formatted arguments.

        Returns
        -------
        s (str) or None
            The tree diagram (as a string) or None if prinout = True, in which
            case the tree diagram is printed in the console.

        '''

        accept_kwargs = ['extend', 'split', 'space', 'final',
                         'folderstart', 'filestart']

        if any(i not in accept_kwargs for i in kwargs.keys()):
            raise FakedirError('kwargs must be any of {}'.format(accept_kwargs))

        if style:
            styleargs = printing.get_styleargs(style)

        styleargs = printing.format_indent(styleargs, indent=indent)

        if uniform is not None:
            for arg in ['extend', 'split', 'final', 'space']:
                styleargs[arg] = uniform

        if anystart is not None:
            styleargs['folderstart'] = anystart
            styleargs['filestart'] = anystart

        for k in kwargs:
            if k in styleargs:
                styleargs[k] = kwargs[k]

        if sort_key is not None or sort_reverse == True:
            sort = True

        if slash.lower() in ['sep', 'os.sep']:
            slash = os.sep

        s = FakeDirStructure(self,
                             depthlimit=depthlimit,
                             itemlimit=itemlimit,
                             beyond=beyond,
                             first=first,
                             sort=sort,
                             sort_reverse=sort_reverse,
                             sort_key=sort_key,
                             include_folders=include_folders,
                             exclude_folders=exclude_folders,
                             include_files=include_files,
                             exclude_files=exclude_files,
                             regex=regex,
                             slash=slash,
                             mask=mask,
                             formatter=formatter,
                             **styleargs).strip()

        if printout:
            print(s)
        else:
            return s
예제 #7
0
    def delete(self, child):
        '''
        Delete items from a `seedir.fakedir.FakeDir`.

        ```
        >>> import seedir as sd
        >>> r = sd.randomdir(seed=5)
        >>> r
        MyFakeDir/
        ├─senor.txt
        ├─verb.txt
        ├─takeoff.txt
        ├─monastic/
        │ ├─paddy.txt
        │ ├─ewe.txt
        │ ├─advantage.txt
        │ ├─hooves/
        │ └─registrar/
        └─zag/
          ├─thematic.txt
          ├─inelastic.txt
          ├─fierce.txt
          ├─gout/
          └─stein/
            ├─vector.txt
            ├─sora.txt
            └─proviso.txt

        >>> r['zag'].delete(['thematic.txt', 'inelastic.txt']) # delete with string names
        >>> r.delete(r['monastic']) # delete with objects
        >>> r
        MyFakeDir/
        ├─senor.txt
        ├─verb.txt
        ├─takeoff.txt
        └─zag/
          ├─fierce.txt
          ├─gout/
          └─stein/
            ├─vector.txt
            ├─sora.txt
            └─proviso.txt

        ```

        Parameters
        ----------
        child : str, FakeDir, FakeFile or list-like
            Child or children to remove.  Can be a string name, actual
            `seedir.fakedir.FakeDir` / `seedir.fakedir.FakeFile` object,
            or a collection of names or items.

        Raises
        ------
        FakedirError
            No item found to delete.

        Returns
        -------
        None.

        '''
        target = None
        if type(child) in [FakeDir, FakeFile]:
            target = child.name
        elif isinstance(child, str):
            target = child
        if target is not None:
            try:
                to_del = next(f for f in self._children if f.name == target)
                to_del.parent = None
            except StopIteration:
                raise FakedirError('{} has no child with name "{}"'.format(self, target))
        else:
            child_copy = [c for c in child]
            for c in child_copy:
                target = None
                if type(c) in [FakeDir, FakeFile]:
                    target = c.name
                elif isinstance(c, str):
                    target = c
                if target is not None:
                    try:
                        to_del = next(f for f in self._children if
                                      f.name == target)
                        to_del.parent = None
                    except StopIteration:
                        raise FakedirError('{} has no child with name "{}"'.format(self, target))
예제 #8
0
def fakedir(path, depthlimit=None, itemlimit=None, first=None,
            sort=False, sort_reverse=False, sort_key=None,
            include_folders=None, exclude_folders=None, include_files=None,
            exclude_files=None, mask=None, regex=True):
    '''
    Function for creating a `seedir.fakedir.FakeDir` (representation of a directory)
    from a real system directory.  Rather than immediately representing
    a directory as a string (`seedir.realdir.seedir()`), this function can be used
    to create an editable representation of the directory, or to join one or
    more directories.

    ```
    >>> import seedir as sd
    >>> f = sd.fakedir('.', depthlimit=0)
    >>> type(f)
    <class 'seedir.fakedir.FakeDir'>

    ```

    Parameters
    ----------
    path : str
        System path of a directory.
    depthlimit : int, optional
        Limit on the depth of directories to traverse. Folders at the depth
        limit will be included, but their contents will not be.
        The default is `None`.
    itemlimit : int, optional
        Limit on the number of items to include per directory.
        The default is `None`, meaning all items will be added.  The priority
        of items is determined by `os.listdir()`, unless sorting arguments
        are passed.
    first : 'folders' or 'files', optional
        Sort to show folders or files first. The default is `None`.
    sort : bool, optional
        Apply a (name) sort on each directory. The default is `False`.
    sort_reverse : bool, optional
        Reverse the sort. The default is `False`.
    sort_key : function, optional
        Key function for sorting the file names. The default is `None`.
    include_folders, exclude_folders, include_files, exclude_files : str, list-like, or None, optional
            Folder / file names to include or exclude. The default is `None`.
    mask : function, optional
        Function for filtering items.  Absolute paths of each individual item
        are passed to the mask function.  If `True` is returned, the
        item is kept.  The default is `None`.
    regex : bool, optional
        Interpret include/exclude folder/file arguments as regular
        expressions. The default is `False`.

    Raises
    ------
    FakedirError
        path does not point to a directory.

    Returns
    -------
    output : seedir.fakedir.FakeDir
        Fake directory matching the path.

    '''
    if not os.path.isdir(path):
        raise FakedirError('path must be a directory')
    output = FakeDir(os.path.basename(path))
    recursive_add_fakes(path, parent=output, depthlimit=depthlimit,
                        itemlimit=itemlimit,
                        first=first,
                        sort=sort,
                        sort_reverse=sort_reverse,
                        sort_key=sort_key,
                        include_folders=include_folders,
                        exclude_folders=exclude_folders,
                        include_files=include_files,
                        exclude_files=exclude_files,
                        mask=mask, regex=regex)
    return output
예제 #9
0
def filter_fakeitem_names(listdir,
                          include_folders=None,
                          exclude_folders=None,
                          include_files=None,
                          exclude_files=None,
                          regex=False,
                          mask=None):
    '''
    Filter for folder and file names in seedir.FakeDir.seedir().  Removes or
    includes items matching filtering strings.

    Parameters
    ----------
    listdir : list-like
        Collection of FakeDir and FakeFile objects
    include_folders : str or list-like, optional
        Folder names to include. The default is None.
    exclude_folders : str or list-like, optional
        Folder names to exclude. The default is None.
    include_files : str or list-like, optional
        File names to include. The default is None.
    exclude_files : str or list-like, optional
        File names to exclude. The default is None.
    mask : function, optional
        Function for filtering items.  Each individual item object
        are passed to the mask function.  If True is returned, the
        item is kept.  The default is None.
    regex : bool, optional
        Interpret strings as regular expressions. The default is False.

    Raises
    ------
    SeedirError
        When the exlcusion or inclusion arguments are not strings or
        iterables.

    Returns
    -------
    keep : list
        Filtered input.

    '''
    keep = []
    for l in listdir:
        if mask is not None:
            if mask(l) is not True:
                continue

        if l.isdir():
            if isinstance(include_folders, str):
                if not printing.is_match(include_folders, l.name, regex):
                    continue
            elif include_folders is not None:
                try:
                    if not any(
                            printing.is_match(n, l.name, regex)
                            for n in include_folders):
                        continue
                except:
                    raise FakedirError('Failure when trying to iterate '
                                       'over "include_folders" and '
                                       'match strings')
            if isinstance(exclude_folders, str):
                if printing.is_match(exclude_folders, l.name, regex):
                    continue
            elif exclude_folders is not None:
                try:
                    if any(
                            printing.is_match(x, l.name, regex)
                            for x in exclude_folders):
                        continue
                except:
                    raise FakedirError('Failure when trying to iterate '
                                       'over "exclude_folders" and '
                                       'match strings')
        else:
            if isinstance(include_files, str):
                if not printing.is_match(include_files, l.name, regex):
                    continue
            elif include_files is not None:
                try:
                    if not any(
                            printing.is_match(n, l.name, regex)
                            for n in include_files):
                        continue
                except:
                    raise FakedirError('Failure when trying to iterate '
                                       'over "include_files" and '
                                       'match strings')
            if isinstance(exclude_files, str):
                if printing.is_match(exclude_files, l.name, regex):
                    continue
            elif exclude_files is not None:
                try:
                    if any(
                            printing.is_match(x, l.name, regex)
                            for x in exclude_files):
                        continue
                except:
                    raise FakedirError('Failure when trying to iterate '
                                       'over "exclude_files" and '
                                       'match strings')
        keep.append(l)
    return keep