def is_file(*pathnames: str) -> bool: ''' ``True`` only if all passed paths are existing non-directory files *after* following symbolic links. If any such path does *not* exist, this function silently ignores this path rather than raising an exception. Versus :func:`path.isfile` ---------- This function intrinsically differs from the standard :func:`path.isfile` function. While the latter returns ``True`` only for non-special files and hence `False` for all non-directory special files (e.g., device nodes, sockets), this function returns `True` for *all* non-directory files regardless of whether these files are special or not. **Why?** Because this function complies with POSIX semantics, whereas :func:`path.isfile` does *not*. The specialness of non-directory files is usually irrelevant; in general, it only matters whether these files are directories or not. For example, the external command ``rm`` removes only non-directory files (regardless of specialness) while the external command ``rmdir`` removes only empty directories. Parameters ---------- pathnames: tuple[str] Tuple of all paths to be tested. Returns ---------- bool ``True`` only if all such paths are existing non-directory files *after* following symbolic links. ''' # Avoid circular import dependencies. from betse.util.path import dirs, paths # Return true only if... return any( # This path both exists and is *NOT* a directory... paths.is_path(pathname) and not dirs.is_dir(pathname) # For each such path. for pathname in pathnames)
def is_worktree(dirname: str) -> bool: ''' ``True`` only if the directory with the passed pathname is a **Git working tree** (i.e., contains a ``.git`` subdirectory). Parameters ---------- dirname : str Relative or absolute pathname of the directory to be tested. Returns ---------- bool ``True`` only if this directory is a Git working tree. ''' # Avoid circular import dependencies. from betse.util.path import dirs, pathnames # Absolute or relative pathname of this directory's ".git" subdirectory. git_subdirname = pathnames.join(dirname, '.git') # Return True only if this subdirectory exists. return dirs.is_dir(git_subdirname)
def _run_pyinstaller_commands(self): ''' Run the PyInstaller command previously constructed by the :meth:`_init_pyinstaller_command` method for each entry point. ''' # Defer heavyweight imports. from betse.util.path import dirs, paths, pathnames # True if the current distribution has at least one entry point. is_entry_point = False # Freeze each previously installed script wrapper. for script_basename, script_type, entry_point in\ supcommand.iter_command_entry_points(self): # Note at least one entry point to be installed. is_entry_point = True #FIXME: We went to a great deal of trouble to implement this method. #Why don't we call it anymore, again? # Validate this wrapper's entry point. # freeze._check_entry_point(entry_point) # Relative path of the output frozen executable file or directory, # created by stripping the suffixing ".exe" filetype on Windows. frozen_pathname = pathnames.join( self.dist_dir, pathnames.get_pathname_sans_filetype(script_basename)) # If cleaning *AND* this path exists, remove this path before # validating this path. Why? This path could be an existing file # and the current command freezing to a directory (or vice versa), # in which case subsequent validation would raise an exception. if self.clean and paths.is_path(frozen_pathname): #FIXME: Define a new paths.remove_path() function resembling #the existing buputils.remove_path() function. paths.remove_path(frozen_pathname) # Validate this path. self._check_frozen_path(frozen_pathname) # Set all environment variables used to communicate with the BETSE- # specific PyInstaller specification file run below. self._set_environment_variables(script_basename, script_type, entry_point) # Run this PyInstaller command for this entry point. self._run_pyinstaller_command(script_basename, script_type, entry_point) # Report these results to the user. frozen_pathtype = ('directory' if dirs.is_dir(frozen_pathname) else 'file') print('Froze {} "{}".\n'.format(frozen_pathtype, frozen_pathname)) #FIXME: Excise when beginning GUI work. break # If no entry points are registered for the current distribution, raise # an exception. if not is_entry_point: raise DistutilsExecError('No entry points found. {}'.format( freeze.EXCEPTION_ADVICE))
def get_package_worktree_dirname_or_none( package: ModuleType) -> StrOrNoneTypes: ''' **Absolute canonical dirname** (i.e., absolute dirname after resolving symbolic links) of the **Git-based working tree** (i.e., top-level directory containing the canonical ``.git`` subdirectory) governing the passed top-level Python package if found *or* ``None`` otherwise. Caveats ---------- **This directory typically does not exist.** This directory is only required during development by developers and hence should *not* be assumed to exist. For safety and efficiency, this function does *not* recursively search the filesystem up from the directory yielding this package for a ``.git`` subdirectory; rather, this function only directly searches that directory itself for such a subdirectory. For that reason, this function typically returns ``None`` for *all* modules except top-level packages installed in a developer manner (e.g., by running ``sudo python3 setup.py develop``). Parameters ---------- package : ModuleType Top-level Python package to be queried. Returns ---------- StrOrNoneTypes Either: * The absolute canonical dirname of that package's Git working tree if that package was installed in a developer manner: e.g., by * ``python3 setup.py develop``. * ``python3 setup.py symlink``. * ``None`` if that package was installed in a non-developer manner: e.g., by * ``pip3 install``. * ``python3 setup.py develop``. ''' # Avoid circular import dependencies. from betse.util.path import dirs, pathnames from betse.util.py.module import pymodule # Absolute canonical dirname of the directory providing this package, # canonicalized into a directory rather than symbolic link to increase the # likelihood of obtaining the actual parent directory of this package package_dirname = pymodule.get_dirname_canonical(package) # Absolute dirname of the parent directory of this directory. worktree_dirname = pathnames.get_dirname(package_dirname) # Absolute dirname of the ".git" subdirectory of this parent directory. git_subdirname = pathnames.join(worktree_dirname, '.git') # Return this parent directory's absolute dirname if this subdirectory # exists *OR* "None" otherwise. return worktree_dirname if dirs.is_dir(git_subdirname) else None
def copy( # Mandatory parameters. src_filename: str, trg_filename: str, # Optional parameters. is_overwritable: bool = False, ) -> None: ''' Copy the passed source file to the passed target file or directory, raising an exception if that target file already exists unless the ``is_overwritable`` parameter is explicitly passed as ``True``. If the: * Source file is a symbolic link, this link (rather than its transitive target) will be copied and hence preserved. * Target file is a directory, the basename of the source file will be appended to this directory -- much like the standard ``cp`` POSIX command. For safety, the target file is: * Copied in a manner maximally preserving *all* existing metadata of the source file. This includes owner, group, permissions, times (e.g., access, creation, modification), and extended attributes (if any). * *Not* overwritten by default. If this file already exists, an exception is raised unless the ``is_overwritable`` parameter is explicitly passed as ``True``. For brevity, consider calling the :func:`copy_overwritable`. Parameters ---------- src_filename : str Absolute or relative path of the source file to be copied from. trg_filename : str Absolute or relative path of the target file to be copied to. is_overwritable : optional[bool] If this target file already exists and this boolean is ``True``, this file is silently overwritten; else, an exception is raised. Defaults to ``False``. Raises ---------- BetseFileException If either the: * Source file does not exist. * Target file already exists *and* ``is_overwritable`` is ``False``. See Also ---------- :func:`copy_overwritable` Higher-level function wrapping this lower-level function by unconditionally passing ``is_overwritable`` parameter as ``True``. ''' # Avoid circular import dependencies. from betse.util.path import dirs, paths, pathnames # Log this copy. logs.log_debug('Copying file: %s -> %s', src_filename, trg_filename) # If this source file does *NOT* exist, raise an exception. die_unless_file(src_filename) # If this target file is a directory, append the basename of the passed # source file to this directory -- much like the "cp" POSIX command. if dirs.is_dir(trg_filename): trg_filename = pathnames.join(trg_filename, pathnames.get_basename(src_filename)) # If this target file already exists but is *NOT* overwritable, raise an # exception. if not is_overwritable: paths.die_if_path(trg_filename) # Create the directory containing this target file *BEFORE* calling the # shutil.copy2() function, which assumes this directory to exist. dirs.make_parent_unless_dir(trg_filename) # Perform this copy in a manner preserving metadata and symbolic links. shutil.copy2(src_filename, trg_filename, follow_symlinks=False)