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
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()
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)
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)
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)
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
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))
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
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