def __init__(self,
                 pizza_cutter_path_conf_file: pathlib.Path = pathlib.Path(__file__).parent.resolve(),
                 pizza_cutter_path_template_dir: Optional[pathlib.Path] = None,
                 pizza_cutter_path_target_dir: Optional[pathlib.Path] = None):
        super().__init__(pizza_cutter_path_conf_file, pizza_cutter_path_template_dir, pizza_cutter_path_target_dir)

# ##############################################################################################################################################################
# PizzaCutterConfiguration
# ##############################################################################################################################################################

        self.pizza_cutter_allow_overwrite = False
        self.pizza_cutter_allow_outside_write = False
        self.pizza_cutter_allow_outside_read = False

        # redefine for doctest
        self.pizza_cutter_options['delete_line_if_empty'] = '{{TestPizzaCutter.option.delete_line_if_empty}}'  # the line will be deleted if empty
        self.pizza_cutter_options['object_no_copy'] = '{{TestPizzaCutter.option.no_copy}}'
        self.pizza_cutter_options['object_no_overwrite'] = '{{TestPizzaCutter.option.no_overwrite}}'

        # redefine for doctest
        self.pizzacutter_pattern_prefixes = ['{{PizzaCutter.', '{{cookiecutter.', '{{TestPizzaCutter']

# ##############################################################################################################################################################
# Project Configuration - single point for all configuration of the project
# ##############################################################################################################################################################
        # the name of the project, for instance for the travis repo slug
        path_test_dir = pathlib.Path(__file__).parent.parent.resolve()
        outside_target_dir = path_test_dir / 'outside_target_dir'
        self.project_dir = 'pizzacutter_test_project'
        self.pizza_cutter_patterns['{{TestPizzaCutter.project_dir}}'] = self.project_dir
        self.pizza_cutter_patterns['{{TestPizzaCutter.doctest}}'] = 'doctest'
        self.pizza_cutter_patterns['{{TestPizzaCutter.outside_target_dir}}'] = outside_target_dir
Esempio n. 2
0
def test_interaction_with_original_pathlib() -> None:
    pathlib_original_file = pathlib_original.Path(
        'some_path/some_path/some_path.txt')
    pathlib3x_file = pathlib.Path('some_path3x/some_path3x/some_path3x.txt')
    assert pathlib.Path.is_path_instance(pathlib_original_file)
    assert pathlib.Path.is_path_instance(pathlib3x_file)
    test_conversion = pathlib.Path(pathlib_original_file)
    isinstance(test_conversion, pathlib.Path)
Esempio n. 3
0
 def test_append_suffix_ok(self) -> None:
     # setup
     path_without_suffix = pathlib.Path('path_without_suffix')
     path_with_suffix = pathlib.Path('path_with.suffix')
     path_without_suffix_appended = path_without_suffix.append_suffix(
         '.test')
     path_with_suffix_appended = path_with_suffix.append_suffix('.test')
     # tests
     self.assertEqual(path_without_suffix_appended.suffix, '.test')
     self.assertEqual(path_without_suffix_appended.suffixes, ['.test'])
     self.assertEqual(path_with_suffix_appended.suffix, '.test')
     self.assertEqual(path_with_suffix_appended.suffixes,
                      ['.suffix', '.test'])
     # test empty suffix
     self.assertEqual(
         pathlib.Path('some_path').append_suffix(''),
         pathlib.Path('some_path'))
Esempio n. 4
0
    def test_append_suffix_sep_or_altsep_in_suffix_raises(self) -> None:
        # Setup
        path_test = pathlib.Path('path_test')
        suffix_with_sep = '.test' + os.path.sep + 'test'

        # Test
        self.assertRaises(ValueError, path_test.append_suffix, suffix_with_sep)
        if os.path.altsep:  # altsep is '/' on Windows
            suffix_with_altsep = '.test' + os.path.altsep + 'test'
            self.assertRaises(ValueError, path_test.append_suffix,
                              suffix_with_altsep)
Esempio n. 5
0
def build(conf_file: str,
          template_dir: str = '',
          target_dir: str = '',
          dry_run: bool = False,
          overwrite: bool = False,
          write_outside: bool = False) -> None:
    """ Builds the Project from the Template

    >>> # Setup
    >>> path_test_dir = pathlib.Path(__file__).parent.parent.resolve() / 'tests'
    >>> path_template_dir = path_test_dir / 'pizzacutter_test_template_01'
    >>> path_target_dir = path_test_dir / 'pizzacutter_test_project_01_result'
    >>> path_conf_file = path_template_dir / 'PizzaCutterTestConfig_01.py'

    >>> # Test only pass "conf_file", dry run
    >>> build(conf_file=str(path_conf_file), template_dir='', target_dir='', dry_run=True)

    >>> # Test pass "conf_file", "template_dir" and "target_dir" dry run
    >>> build(conf_file=str(path_conf_file), template_dir=str(path_template_dir), target_dir=str(path_target_dir), dry_run=True)

    """

    path_conf_file = pathlib.Path(conf_file).resolve()

    if template_dir:
        path_template_dir = pathlib.Path(template_dir).resolve()
    else:
        path_template_dir = path_conf_file.parent

    if target_dir:
        path_target_dir = pathlib.Path(target_dir).resolve()
    else:
        path_target_dir = pathlib.Path.cwd().resolve()

    pizzacutter.build(path_conf_file=path_conf_file,
                      path_template_dir=path_template_dir,
                      path_target_dir=path_target_dir,
                      dry_run=dry_run,
                      allow_overwrite=overwrite,
                      allow_outside_write=write_outside)
Esempio n. 6
0
def test_shutil_wrappers() -> None:
    """ test the shutil wrappers """
    path_test_dir = pathlib.Path(__file__).parent.resolve()
    path_test_file = path_test_dir / 'test.txt'
    path_target_file = path_test_dir / 'test_target.txt'
    path_test_tree = path_test_dir / 'test_treecopy'
    path_test_tree_target = path_test_dir / 'test_treecopy_target'
    path_test_file.copy(path_target_file)
    path_test_file.copy2(path_target_file)
    path_test_file.copyfile(path_target_file)
    path_test_file.copymode(path_target_file)
    path_test_file.copystat(path_target_file)
    path_test_tree.copytree(path_test_tree_target)
    path_test_tree_target.rmtree()
    path_target_file.unlink()
Esempio n. 7
0
    def path_remove_cutter_option_patterns(
            self, path_source_file: pathlib.Path) -> pathlib.Path:
        """
        removes option patterns from the filename - those are already checked and considered earlier

        >>> # Setup
        >>> path_test_dir = pathlib.Path(__file__).parent.parent / 'tests'
        >>> path_template_dir = path_test_dir / 'pizzacutter_test_template_01'
        >>> path_conf_file = path_template_dir / 'PizzaCutterTestConfig_01.py'
        >>> path_target_dir = path_test_dir / 'pizzacutter_test_project_01'
        >>> pizza_cutter = PizzaCutter(path_conf_file, path_template_dir, path_target_dir)

        >>> # test ok
        >>> test_file = path_template_dir/ 'test.txt{{TestPizzaCutter.option.no_copy}}'
        >>> pizza_cutter.path_remove_cutter_option_patterns(test_file)
        <BLANKLINE>
        ...Path('.../tests/pizzacutter_test_template_01/test.txt')

        >>> # directory only option patterns Fails
        >>> test_file = path_template_dir/ '{{TestPizzaCutter.option.no_copy}}/test.txt{{TestPizzaCutter.option.no_copy}}'
        >>> pizza_cutter.path_remove_cutter_option_patterns(test_file)
        Traceback (most recent call last):
            ...
        RuntimeError: No part of the path ...

        >>> # File only option patterns Fails
        >>> test_file = path_template_dir/ '{{TestPizzaCutter.option.no_copy}}.test/{{TestPizzaCutter.option.no_copy}}'
        >>> pizza_cutter.path_remove_cutter_option_patterns(test_file)
        Traceback (most recent call last):
            ...
        RuntimeError: No part of the path ...

        """
        source_file_parts = path_source_file.parts
        result_file_parts = list()
        for source_file_part in source_file_parts:
            for option_pattern in self.conf.pizza_cutter_options.values():
                source_file_part = source_file_part.replace(option_pattern, '')
            if not source_file_part:
                raise RuntimeError(
                    f'No part of the path must consist ONLY of option patterns: "{path_source_file}"'
                )
            result_file_parts.append(source_file_part)
        result_path_source_file = pathlib.Path(*result_file_parts)
        return result_path_source_file
Esempio n. 8
0
    def path_replace_string_patterns(
            self, path_source_object_resolved: pathlib.Path) -> pathlib.Path:
        """
        replaces string patterns in the filename

        >>> # Setup
        >>> path_test_dir = pathlib.Path(__file__).parent.parent / 'tests'
        >>> path_template_dir = path_test_dir / 'pizzacutter_test_template_01'
        >>> path_conf_file = path_template_dir / 'PizzaCutterTestConfig_01.py'
        >>> path_target_dir = path_test_dir / 'pizzacutter_test_project_01'
        >>> pizza_cutter = PizzaCutter(path_conf_file, path_template_dir, path_target_dir)

        >>> # test no replacements
        >>> test_file = path_template_dir/ 'test.txt'
        >>> pizza_cutter.path_replace_string_patterns(test_file)
        <BLANKLINE>
        ...Path('.../tests/pizzacutter_test_template_01/test.txt')

        >>> # test with replacement
        >>> pizza_cutter.conf.pizza_cutter_patterns['{{TestPizzaCutter.doctest}}'] = 'doctest'
        >>> test_file = path_template_dir/ 'test_{{TestPizzaCutter.doctest}}.txt'
        >>> pizza_cutter.path_replace_string_patterns(test_file)
        <BLANKLINE>
        ...Path('.../tests/pizzacutter_test_template_01/test_doctest.txt')


        """
        source_file_parts = path_source_object_resolved.parts
        result_file_parts = list()
        for source_file_part in source_file_parts:
            for pattern in self.conf.pizza_cutter_patterns.keys():
                replacement = self.conf.pizza_cutter_patterns[pattern]
                if isinstance(replacement, str):
                    source_file_part = source_file_part.replace(
                        pattern, replacement)
            result_file_parts.append(source_file_part)
        result_path_source_file = pathlib.Path(*result_file_parts)
        return result_path_source_file
Esempio n. 9
0
def pizza_cutter_instance():
    path_test_dir = pathlib.Path(__file__).parent.parent.resolve() / 'tests'
    path_template_dir = path_test_dir / 'pizzacutter_test_template_02'
    path_conf_file = path_template_dir / 'PizzaCutterTestConfig_02.py'
    path_target_dir = path_test_dir / 'test_target'
    path_outside_target_dir = get_outside_target_dir()

    pizza_cutter = pizzacutter.PizzaCutter(path_conf_file=path_conf_file,
                                           path_template_dir=path_template_dir,
                                           path_target_dir=path_target_dir)
    yield pizza_cutter  # provide the fixture value
    # teardown code
    if not path_target_dir.is_relative_to(path_test_dir):
        raise RuntimeError(
            'attempt to delete "{}" which is outside the test dir "{}"'.format(
                pizza_cutter_instance.path_target_dir, path_test_dir))

    if not path_target_dir.is_relative_to(path_test_dir):
        raise RuntimeError(
            'attempt to delete "{}" which is outside the test dir "{}"'.format(
                path_outside_target_dir, path_test_dir))

    shutil.rmtree(path_target_dir, ignore_errors=True)
    shutil.rmtree(path_outside_target_dir, ignore_errors=True)
Esempio n. 10
0
    def __init__(
            self,
            # the path to the PizzaCutter conf File
            path_conf_file: pathlib.Path,
            # the path to the Template Folder - can be set by the conf File to the Directory the conf file sits - can be overridden by untrusted conf_file
            path_template_dir: Optional[pathlib.Path] = None,
            # the target path of the Project Folder - this should be the current Directory - can be overridden by conf_file
            path_target_dir: Optional[pathlib.Path] = None,
            # dry run - test only, report overwrites, files outside project directory, unset patterns, unused patterns from conf file
            # only made the easy tests now - for full test of replacements we would need to install into a temp directory
            dry_run: Optional[bool] = None,
            # allow overwrite in the target Project, can be overridden by conf_file
            allow_overwrite: Optional[bool] = None,
            # allow to write files outside of the target Project Folder, can be overridden by conf_file
            allow_outside_write: Optional[bool] = None,
            quiet: Optional[bool] = None):
        """ Init reads the config file and sets up the neccessary class properties

        >>> # Setup
        >>> path_test_dir = pathlib.Path(__file__).parent.parent.resolve() / 'tests'
        >>> path_template_dir = path_test_dir / 'pizzacutter_test_template_01'
        >>> path_conf_file = path_template_dir / 'PizzaCutterTestConfig_01.py'

        >>> # Test init, only conf file passed, quiet
        >>> pizza_cutter = PizzaCutter(path_conf_file=path_conf_file, quiet=True)

        >>> # Test init, conf file not found
        >>> pizza_cutter = PizzaCutter(path_conf_file=pathlib.Path(), quiet=True)
        Traceback (most recent call last):
        ...
        FileNotFoundError: the config file ... can not be found

        """

        if not path_conf_file.is_file():
            raise FileNotFoundError(
                f'the config file "{path_conf_file}" can not be found')

        self.conf = get_config.PizzaCutterGetConfig(
            pizza_cutter_path_conf_file=path_conf_file,
            pizza_cutter_path_template_dir=path_template_dir,
            pizza_cutter_path_target_dir=path_target_dir).conf

        if path_template_dir is None:
            # we call again pathlib.Path, to be sure it is pathlib3x Type
            self.path_template_dir = pathlib.Path(
                self.conf.pizza_cutter_path_template_dir)
        else:
            self.path_template_dir = pathlib.Path(path_template_dir)

        if path_target_dir is None:
            self.path_target_dir = pathlib.Path(
                self.conf.pizza_cutter_path_target_dir)
        else:
            self.path_target_dir = pathlib.Path(path_target_dir)

        if allow_overwrite is None:
            self.allow_overwrite = self.conf.pizza_cutter_allow_overwrite
        else:
            self.allow_overwrite = allow_overwrite

        if allow_outside_write is None:
            self.allow_outside_write = self.conf.pizza_cutter_allow_outside_write
        else:
            self.allow_outside_write = allow_outside_write

        if dry_run is None:
            self.dry_run = self.conf.pizza_cutter_dry_run
        else:
            self.dry_run = dry_run

        if quiet is None:
            self.quiet = self.conf.pizza_cutter_quiet
        else:
            self.quiet = quiet

        self.file_stack: List[pathlib.Path] = list()
        self.pattern_stack: List[str] = list()
Esempio n. 11
0
def import_module_from_file(module_fullpath: Union[pathlib.Path, str], reload: bool = False):   # type: ignore
    """
    TODO : replace with lib_import when avail maybe take from pycharm, there we do the full coverage ...

    >>> # re-import from file
    >>> import_module_from_file(pathlib.Path(__file__))
    <module 'import_module' from '...import_module.py'>

    >>> # re-import from file without extension
    >>> import_module_from_file(pathlib.Path(__file__).with_suffix(''))
    <module 'import_module' from '...import_module.py'>

    >>> # re-import from file, reload = True
    >>> import_module_from_file(pathlib.Path(__file__), reload=True)
    <module 'import_module' from '...import_module.py'>

    >>> # import from non-existing file, and invalid module name. reload = True

    >>> # re-import from non-existing file, but already imported module name. reload = True
    >>> import_module_from_file(pathlib.Path(__file__).with_suffix('.non_existing'), reload=True)
    Traceback (most recent call last):
    ...
    FileNotFoundError: module "...import_module.non_existing.py" not found

    """
    module_fullpath = pathlib.Path(module_fullpath)

    if not module_fullpath.suffix == '.py':
        module_fullpath = pathlib.Path(str(module_fullpath) + '.py')

    if not module_fullpath.is_file():
        raise FileNotFoundError('module "{}" not found'.format(module_fullpath))

    module_name = module_fullpath.stem

    if not reload and module_name in sys.modules:
        return sys.modules[module_name]

    if reload:
        # see https://docs.python.org/3/library/importlib.html
        importlib.invalidate_caches()

    sys.path.append(str(module_fullpath.parent))

    spec = importlib.util.spec_from_file_location(module_name, module_fullpath)
    if spec is None:
        sys.path.pop()
        raise ImportError('can not get spec from file location "{}"'.format(module_fullpath))

    try:
        mod = importlib.util.module_from_spec(spec)
        sys.modules[module_name] = mod
    except Exception as exc:
        raise ImportError('can not load module "{}"'.format(module_name)) from exc
    finally:
        sys.path.pop()
        sys.path.append(str(module_fullpath.parent))

    try:
        spec.loader.exec_module(mod)    # type: ignore
    except Exception as exc:
        sys.path.pop()
        raise ImportWarning('module "{}" reloaded, but can not be executed'.format(module_name)) from exc

    return mod
Esempio n. 12
0
 def test_unlink_missing_ok(self) -> None:
     pathlib.Path('__not_existing__').unlink(missing_ok=True)
Esempio n. 13
0
 def test_append_suffix_invalid_suffix_raises(self) -> None:
     path_test = pathlib.Path('some_path')
     # test suffix not starting with '.'
     self.assertRaises(ValueError, path_test.append_suffix, 'test')
     # test suffix is only '.'
     self.assertRaises(ValueError, path_test.append_suffix, '.')
Esempio n. 14
0
 def test_glob(self) -> None:
     pathlib.Path('.').glob('**/*')
Esempio n. 15
0
def get_outside_target_dir() -> pathlib.Path:
    path_test_dir = pathlib.Path(__file__).parent.parent.resolve() / 'tests'
    outside_target_dir = path_test_dir / 'outside_target_dir'
    return outside_target_dir
Esempio n. 16
0
    def __init__(self,
                 # the path to the actual config File
                 pizza_cutter_path_conf_file: Optional[pathlib.Path] = None,
                 # the default for the template Folder is the actual directory of the given config File
                 pizza_cutter_path_template_dir: Optional[pathlib.Path] = None,
                 # the project Folder is the current directory
                 pizza_cutter_path_target_dir: Optional[pathlib.Path] = None):
        """
        The Base Class for the Pizza Cutter Configuration

        >>> # Test Config File not found
        >>> config = PizzaCutterConfigBase(pizza_cutter_path_conf_file=pathlib.Path('not_existing_conf_file'))
        Traceback (most recent call last):
        ...
        FileNotFoundError: PizzaCutter config file "not_existing_conf_file" does not exist

        >>> # Test Config File not found
        >>> config = PizzaCutterConfigBase(pizza_cutter_path_template_dir=pathlib.Path('not_existing_template_directory'))
        Traceback (most recent call last):
        ...
        NotADirectoryError: Template Directory "not_existing_template_directory" must be an existing Directory

        """

        if pizza_cutter_path_conf_file is None:
            pizza_cutter_path_conf_file = pathlib.Path(__file__).resolve()
        else:
            if not pizza_cutter_path_conf_file.is_file():
                raise FileNotFoundError('PizzaCutter config file "{}" does not exist'.format(pizza_cutter_path_conf_file))
            # make sure it is a pathlib3x object
            pizza_cutter_path_conf_file = pathlib.Path(pizza_cutter_path_conf_file)
            pizza_cutter_path_conf_file = pizza_cutter_path_conf_file.resolve()

        if pizza_cutter_path_template_dir is None:
            pizza_cutter_path_template_dir = pizza_cutter_path_conf_file.resolve().parent
        else:
            if not pizza_cutter_path_template_dir.is_dir():
                raise NotADirectoryError('Template Directory "{}" must be an existing Directory'.format(pizza_cutter_path_template_dir))
            # make sure it is a pathlib3x object
            pizza_cutter_path_template_dir = pathlib.Path(pizza_cutter_path_template_dir)
            pizza_cutter_path_template_dir = pizza_cutter_path_template_dir.resolve()

        if pizza_cutter_path_target_dir is None:
            pizza_cutter_path_target_dir = pathlib.Path.cwd().resolve()
        else:
            # make sure it is a pathlib3x object
            pizza_cutter_path_target_dir = pathlib.Path(pizza_cutter_path_target_dir)

        self.pizza_cutter_path_conf_file = pizza_cutter_path_conf_file
        self.pizza_cutter_path_template_dir = pizza_cutter_path_template_dir
        self.pizza_cutter_path_target_dir = pizza_cutter_path_target_dir
        self.pizza_cutter_options: Dict[str, str] = dict()

        # the settings from the CLI can only be overwritten by configuration files
        self.pizza_cutter_allow_overwrite = False
        self.pizza_cutter_allow_outside_write = False
        self.pizza_cutter_dry_run = False
        self.pizza_cutter_quiet = False

        # for patterns to look out after all replacements, in order to find unfilled patterns
        self.pizzacutter_pattern_prefixes = ['{{PizzaCutter', '{{cookiecutter', '{{pizzacutter', '{{Pizzacutter']

        # ######################################################################################################################################################
        # replacement patterns
        # ######################################################################################################################################################

        # replacement patterns can be string, or pathlib.Path Objects - the pathlib Objects can be absolute or relative
        # if You chain such pathlib Objects in template files or directories,, the final destination of the file might be not were You expected.
        # since You might pass relative or absolute paths to the PizzaCutter CLI Application, You should be careful
        # about the resulting paths, especially if You pass absolute paths.

        # beware of differences in Linux and Windows : on Windows pathlib.Path('/test') is relative, on Linux it is absolute !
        # best practice is to use relative paths in the form pathlib.Path('./test')

        # with great flexibility there comes big responsibility. You should test Your Pizzacutter conf_files with absolute and relative Paths

        # for the project path, and check carefully the result. We might disallow absolute paths in the future, or only enable it with a flag,
        # not to allow dangerous Pizzacutter conf_files to overwrite system files.

        # in general, if not really needed on purpose, we would suggest to use only string replacements in directory- and filenames.
        # on the other hand, this feature can be very useful, in order t drop files to the user desktop,
        # user home, windows appdir, etc... OUTSIDE of the Project Path given

        # path replacement patterns are also valid in text files
        # in that case the pattern will be replaced with the content of that file (if found)
        # if the file is not found, or not readable, the string of the path will be filled in. (with a warning)
        # You can even include Files from outside the template Folder, or from the Project Folder itself.

        # Those replacements will be done AFTER the template Files are copied to the target Project, to make sure that even
        # replacements from the target project file work properly.

        # this can be useful for situations like:
        # /template_folder/my_special_configuration{{PizzaCutter.option.no_overwrite}}.txt                          # template for the special configuration
        # /template_folder/some_file.txt        # that file includes /project_folder/my_special_configuration.txt
        # in that case, /project_folder/some_file.txt will include /project_folder/my_special_configuration.txt correctly,
        # even if the project is just created.

        # chaining of only relative Paths :
        # {{PizzaCutter.relative_path_object1}} = pathlib.Path('test1/test2')   # relative path
        # {{PizzaCutter.relative_path_object2}} = pathlib.Path('test3/test4')   # relative path
        # .../template_directory/{{PizzaCutter.relative_path_object1}}/{{PizzaCutter.relative_path_object2}}/test.txt  will work as expected. and resolve to:
        # .../template_directory/test1/test2/test3/test4/test.txt --> .../project_directory/test1/test2/test3/test4/test.txt

        # chaining of Absolute and Relative Paths :
        # {{PizzaCutter.absolute_path_object1}} = pathlib.Path('/test1/test2')  # absolute Path
        # {{PizzaCutter.relative_path_object2}} = pathlib.Path('test3/test4')   # relative path
        # .../template_directory/{{PizzaCutter.absolute_path_object1}}/{{PizzaCutter.relative_path_object2}}/test.txt  will work as expected. and resolve to:
        # /test1/test2/test3/test4/test.txt
        # by that way You might even write configuration files into /usr/etc or similar (depending on Your rights)!

        # unexpected Result when chaining Absolute and Relative Paths in the wrong order :
        # {{PizzaCutter.relative_path_object1}} = pathlib.Path('test1/test2')   # relative Path
        # {{PizzaCutter.absolute_path_object2}} = pathlib.Path('/test3/test4')  # absolute path
        # .../template_directory/{{PizzaCutter.relative_path_object1}}/{{PizzaCutter.absolute_path_object2}}/test.txt will work unexpected and resolve to:
        # /test3/test4/test.txt
        # by that way You might even write configuration files into /usr/etc or similar (depending on Your rights)!

        # ######################################################################################################################################################

        self.pizza_cutter_patterns: Dict[str, Union[str, pathlib.Path]] = dict()
        # this is useful in scripts, to detect if cutting already happened
        # for instance bash:  if [[ "{{PizzaCutter.True}}" == "True" ]]; then ...
        self.pizza_cutter_patterns['{{PizzaCutter.True}}'] = 'True'

        # ######################################################################################################################################################
        # cutter_options
        # ######################################################################################################################################################

        # You might name Your Patterns as You like, for instance CakeCutter, LemonCutter, MelonCutter whatever ;-)
        # even the Names for the Option Flags can be configured here
        # You must not change the keys for that options, those are hardcoded
        self.pizza_cutter_options['delete_line_if_empty'] = '{{PizzaCutter.option.delete_line_if_empty}}'  # the line will be deleted if empty
        # files or directories with that marker that will not be copied to target
        # files within a directory with that marker will not be copied to target, so You dont have to mark each file seperately
        self.pizza_cutter_options['object_no_copy'] = '{{PizzaCutter.option.no_copy}}'
        # files or directories with that marker that will not overwritten on Target
        # files within a directory with that marker will not be overwritten on the target, so You dont have to mark each file seperately
        self.pizza_cutter_options['object_no_overwrite'] = '{{PizzaCutter.option.no_overwrite}}'
Esempio n. 17
0
    def path_replace_pathlib_patterns(
            self, path_source_path: pathlib.Path) -> pathlib.Path:
        """
        Returns the resolved Target Path

        >>> # Setup
        >>> path_test_dir = pathlib.Path(__file__).parent.parent / 'tests'
        >>> path_template_dir = path_test_dir / 'pizzacutter_test_template_01'
        >>> path_conf_file = path_template_dir / 'PizzaCutterTestConfig_01.py'
        >>> path_target_dir = path_test_dir / 'pizzacutter_test_project_01'
        >>> pizza_cutter = PizzaCutter(path_conf_file, path_template_dir, path_target_dir)

        >>> # test absolute replacement + relative replacement
        >>> import platform
        >>> if platform.system().lower() == 'windows':
        ...     pizza_cutter.conf.pizza_cutter_patterns['{{TestPizzaCutter.path.doctest.absolute}}'] = pathlib.Path('c:/test/doctest_absolute')
        ... else:
        ...     pizza_cutter.conf.pizza_cutter_patterns['{{TestPizzaCutter.path.doctest.absolute}}'] = pathlib.Path('/test/doctest_absolute')
        >>> pizza_cutter.conf.pizza_cutter_patterns['{{TestPizzaCutter.path.doctest.relative}}'] = pathlib.Path('./doctest')
        >>> test_file = path_template_dir/ '{{TestPizzaCutter.path.doctest.absolute}}/{{TestPizzaCutter.path.doctest.relative}}/test.txt'
        >>> pizza_cutter.path_replace_pathlib_patterns(test_file)
        <BLANKLINE>
        ...Path('.../doctest_absolute/doctest/test.txt')

        >>> # test no replacements
        >>> test_file = path_template_dir/ 'test.txt'
        >>> pizza_cutter.path_replace_pathlib_patterns(test_file)
        <BLANKLINE>
        ...Path('.../tests/pizzacutter_test_project_01/test.txt')

        >>> # test relative replacements
        >>> pizza_cutter.conf.pizza_cutter_patterns['{{TestPizzaCutter.path.doctest.relative}}'] = pathlib.Path('./doctest')
        >>> test_file = path_template_dir/ '{{TestPizzaCutter.path.doctest.relative}}/{{TestPizzaCutter.path.doctest.relative}}/test.txt'
        >>> pizza_cutter.path_replace_pathlib_patterns(test_file)
        <BLANKLINE>
        ...Path('.../tests/pizzacutter_test_project_01/doctest/doctest/test.txt')

        >>> # test relative replacement + absolute replacement
        >>> if platform.system().lower() == 'windows':
        ...     pizza_cutter.conf.pizza_cutter_patterns['{{TestPizzaCutter.path.doctest.absolute}}'] = pathlib.Path('c:/test/doctest_absolute')
        ... else:
        ...     pizza_cutter.conf.pizza_cutter_patterns['{{TestPizzaCutter.path.doctest.absolute}}'] = pathlib.Path('/test/doctest_absolute')
        >>> pizza_cutter.conf.pizza_cutter_patterns['{{TestPizzaCutter.path.doctest.relative}}'] = pathlib.Path('./doctest')
        >>> test_file = path_template_dir/ '{{TestPizzaCutter.path.doctest.relative}}/{{TestPizzaCutter.path.doctest.absolute}}/test.txt'
        >>> pizza_cutter.path_replace_pathlib_patterns(test_file)
        <BLANKLINE>
        ...Path('.../doctest_absolute/test.txt')

        >>> # test absolute replacement + absolute replacement (last "wins")
        >>> if platform.system().lower() == 'windows':
        ...     pizza_cutter.conf.pizza_cutter_patterns['{{TestPizzaCutter.path.doctest.absolute1}}'] = pathlib.Path('c:/test/doctest_absolute1')
        ... else:
        ...     pizza_cutter.conf.pizza_cutter_patterns['{{TestPizzaCutter.path.doctest.absolute1}}'] = pathlib.Path('/test/doctest_absolute1')

        >>> if platform.system().lower() == 'windows':
        ...     pizza_cutter.conf.pizza_cutter_patterns['{{TestPizzaCutter.path.doctest.absolute2}}'] = pathlib.Path('c:/test/doctest_absolute2')
        ... else:
        ...     pizza_cutter.conf.pizza_cutter_patterns['{{TestPizzaCutter.path.doctest.absolute2}}'] = pathlib.Path('/test/doctest_absolute2')

        >>> test_file = path_template_dir/ '{{TestPizzaCutter.path.doctest.absolute1}}/{{TestPizzaCutter.path.doctest.absolute2}}/test.txt'
        >>> pizza_cutter.path_replace_pathlib_patterns(test_file)
        <BLANKLINE>
        ...Path('.../doctest_absolute2/test.txt')

        >>> # test path replacement not complete part of a path (name is also a complete part !!!)
        >>> pizza_cutter.conf.pizza_cutter_patterns['{{TestPizzaCutter.path.doctest.relative}}'] = pathlib.Path('./doctest')
        >>> test_file = path_template_dir/ '{{TestPizzaCutter.path.doctest.relative}}/{{TestPizzaCutter.path.doctest.relative}}.txt'
        >>> pizza_cutter.path_replace_pathlib_patterns(test_file)
        Traceback (most recent call last):
            ...
        RuntimeError: ... can only be one complete part of a path : ...{{...doctest.relative}}.txt", Pattern: {{TestPizzaCutter.path.doctest.relative}}

        """

        source_object_parts = reversed(path_source_path.parts)
        target_parts = list()
        absolute_path_found = False
        for source_object_part in source_object_parts:
            target_object_part: Union[str, pathlib.Path] = source_object_part
            for pattern in self.conf.pizza_cutter_patterns.keys():
                replacement = self.conf.pizza_cutter_patterns[pattern]
                # we need this, because pathlib3x.Path is NOT instance of pathlib.Path,
                # but the User might use pathlib in his config File !
                if isinstance(replacement, str):
                    continue
                if pattern in source_object_part:
                    if source_object_part != pattern:
                        raise RuntimeError(
                            f'pathlib.Path patterns can only be one complete part of a path : Path: "{path_source_path}", Pattern: {pattern}'
                        )
                    else:
                        target_object_part = pathlib.Path(replacement)
                        if target_object_part.is_absolute(
                        ) and absolute_path_found:
                            logger.warning(
                                'the resulting path might be unexpected, You have more then one absolute pathlib.Path pattern in the path: '
                                f'"{path_source_path}", Pattern: "{pattern}" points to "{replacement}"'
                            )

            if not absolute_path_found:
                target_parts.append(target_object_part)
                if not isinstance(target_object_part, str) and pathlib.Path(
                        target_object_part).is_absolute():
                    absolute_path_found = True

        target_parts = list(reversed(target_parts))
        path_target_path = pathlib.Path(*target_parts).resolve()

        if absolute_path_found:
            if not self.quiet:
                logger.warning(
                    'the resulting path of a template file might be unexpected, You have an absolute pathlib.Path pattern in the path: '
                    f'"{path_source_path}" points to "{path_target_path}"')
        else:
            path_target_path = path_target_path.replace_parts(
                self.path_template_dir.resolve(),
                self.path_target_dir.resolve())
        return path_target_path
Esempio n. 18
0
 def test_append_suffix_empty_name_raises(self) -> None:
     path_with_empty_name = pathlib.Path('')
     self.assertRaises(ValueError, path_with_empty_name.append_suffix,
                       '.test')