Strategies

Strategies do the merging between the files from template and the target.

Strategies are specified in the scaraplate.yaml file located in the root of the template dir.

Sample scaraplate.yaml excerpt:

default_strategy: scaraplate.strategies.Overwrite
strategies_mapping:
  setup.py: scaraplate.strategies.TemplateHash
  src/*/__init__.py: scaraplate.strategies.IfMissing
  package.json:
    strategy: mypackage.mymodule.MyPackageJson
    config:
      my_key: True
  "src/{{ cookiecutter.myvariable }}.md": scaraplate.strategies.IfMissing

The strategy should be an importable Python class which implements Strategy.

default_strategy and strategies_mapping keys are the required ones.

The strategy value might be either a string (specifying a Python class), or a dict of two keys – strategy and config. The first form is just a shortcut for specifying a strategy with an empty config.

config would be passed to the Strategy’s __init__ which would be validated with the inner Schema class.

class scaraplate.strategies.Strategy

Bases: abc.ABC

The abstract base class for a scaraplate Strategy.

To implement and use a custom strategy, the following needs to be done:

  1. Create a new Python class which implements Strategy
  2. Override the inner Schema class if you need to configure your strategy from scaraplate.yaml.
  3. Implement the apply method.

Assuming that the new strategy class is importable in the Python environment in which scaraplate is run, to use the strategy you need to specify it in scaraplate.yaml, e.g.

strategies_mapping:
  myfile.txt: mypackage.mymodule.MyStrategy
class Schema

An empty default schema which doesn’t accept any parameters.

__init__(*, target_contents: Optional[BinaryIO], template_contents: BinaryIO, template_meta: scaraplate.template.TemplateMeta, config: Dict[str, Any]) → None

Init the strategy.

Parameters:
  • target_contents – The file contents in the target project. None if the file doesn’t exist.
  • template_contents – The file contents from the template (after cookiecutter is applied).
  • template_meta – Template metadata, see scaraplate.template.TemplateMeta.
  • config – The strategy config from scaraplate.yaml. It is validated in this __init__ with the inner Schema class.
apply() → BinaryIO

Apply the Strategy.

Returns:The resulting file contents which would overwrite the target file.

Built-in Strategies

class scaraplate.strategies.Overwrite

Bases: scaraplate.strategies.Strategy

A simple strategy which always overwrites the target files with the ones from the template.

class Schema

An empty default schema which doesn’t accept any parameters.

class scaraplate.strategies.IfMissing

Bases: scaraplate.strategies.Strategy

A strategy which writes the file from the template only if it doesn’t exist in target.

class Schema

An empty default schema which doesn’t accept any parameters.

class scaraplate.strategies.SortedUniqueLines

Bases: scaraplate.strategies.Strategy

A strategy which combines both template and target files, sorts the combined lines and keeps only unique ones.

However, the comments in the beginning of the files are treated differently. They would be stripped from the target and replaced with the ones from the template. The most common usecase for this are the License headers.

Sample scaraplate.yaml excerpt:

strategies_mapping:
  MANIFEST.in:
    strategy: scaraplate.strategies.SortedUniqueLines
  .gitignore:
    strategy: scaraplate.strategies.SortedUniqueLines
class Schema

Allowed params:

  • comment_pattern [^ *([;#%]|//)] – a PCRE pattern which should match the line with a comment.
class scaraplate.strategies.TemplateHash

Bases: scaraplate.strategies.Strategy

A strategy which appends to the target file a git commit hash of the template being applied; and the subsequent applications of the same template for this file are ignored until the HEAD commit of the template changes.

This strategy is useful when a file needs to be different from the template but there’s no suitable automated strategy yet, so it should be manually resynced on template updates.

This strategy overwrites the target file on each new commit in the template. There’s also a RenderedTemplateFileHash strategy which does it less frequently: only when the source file from the template has changes.

Sample scaraplate.yaml excerpt:

strategies_mapping:
  setup.py:
    strategy: scaraplate.strategies.TemplateHash
    config:
      line_comment_start: '#'
      max_line_length: 87
      max_line_linter_ignore_mark: '  # noqa'
  Jenkinsfile:
    strategy: scaraplate.strategies.TemplateHash
    config:
      line_comment_start: '//'

This would result in the following:

setup.py:

...file contents...

# Generated by https://github.com/rambler-digital-solutions/scaraplate
# From https://github.com/rambler-digital-solutions/scaraplate-example-template/commit/1111111111111111111111111111111111111111  # noqa

Jenkinsfile:

...file contents...

// Generated by https://github.com/rambler-digital-solutions/scaraplate
// From https://github.com/rambler-digital-solutions/scaraplate-example-template/commit/1111111111111111111111111111111111111111
class Schema

Allowed params:

  • line_comment_start [#] – The prefix which should be used to start a line comment.
  • max_line_length [None] – The maximum line length for the appended line comments after which the max_line_linter_ignore_mark suffix should be added.
  • max_line_linter_ignore_mark [# noqa] – The linter’s line ignore mark for the appended line comments which are longer than max_line_length columns. The default # noqa mark silences flake8.
class scaraplate.strategies.RenderedTemplateFileHash

Bases: scaraplate.strategies.TemplateHash

A strategy which appends to the target file a hash of the rendered template file; and the subsequent applications of the same template for this file are ignored until the rendered file has changes.

This strategy is similar to TemplateHash with the difference that the target file is rewritten less frequently: only when the hash of the source file from the template is changed.

New in version 0.2.

Sample scaraplate.yaml excerpt:

strategies_mapping:
    setup.py:
      strategy: scaraplate.strategies.RenderedTemplateFileHash
      config:
        line_comment_start: '#'
        max_line_length: 87
        max_line_linter_ignore_mark: '  # noqa'
    Jenkinsfile:
      strategy: scaraplate.strategies.RenderedTemplateFileHash
      config:
        line_comment_start: '//'

This would result in the following:

setup.py:

...file contents...

# Generated by https://github.com/rambler-digital-solutions/scaraplate
# RenderedTemplateFileHash d2671228e3dfc3e663bfaf9b5b151ce8
# From https://github.com/rambler-digital-solutions/scaraplate-example-template/commit/1111111111111111111111111111111111111111  # noqa

Jenkinsfile:

...file contents...

// Generated by https://github.com/rambler-digital-solutions/scaraplate
// RenderedTemplateFileHash d2as1228eb7233e663bfaf9b5b151ce8
// From https://github.com/rambler-digital-solutions/scaraplate-example-template/commit/1111111111111111111111111111111111111111
class Schema

Allowed params:

  • line_comment_start [#] – The prefix which should be used to start a line comment.
  • max_line_length [None] – The maximum line length for the appended line comments after which the max_line_linter_ignore_mark suffix should be added.
  • max_line_linter_ignore_mark [# noqa] – The linter’s line ignore mark for the appended line comments which are longer than max_line_length columns. The default # noqa mark silences flake8.
class scaraplate.strategies.ConfigParserMerge

Bases: scaraplate.strategies.Strategy

A strategy which merges INI-like files (with configparser).

The resulting file is the one from the template with the following modifications:

  • Comments are stripped
  • INI file is reformatted (whitespaces are cleaned, sections and keys are sorted)
  • Sections specified in the preserve_sections config list are preserved from the target file.
  • Keys specified in the preserve_keys config list are preserved from the target file.

This strategy cannot be used to merge config files which contain keys without a preceding section declaration (e.g. .editorconfig won’t work).

Sample scaraplate.yaml excerpt:

strategies_mapping:
  .pylintrc:
    strategy: scaraplate.strategies.ConfigParserMerge
    config:
      preserve_sections: []
      preserve_keys:
      - sections: ^MASTER$
        keys: ^extension-pkg-whitelist$
      - sections: ^TYPECHECK$
        keys: ^ignored-

  tox.ini:
    strategy: scaraplate.strategies.ConfigParserMerge
    config:
      preserve_sections:
      - sections: ^tox$
      preserve_keys:
      - sections: ^testenv
        keys: ^extras$

  pytest.ini:
    strategy: scaraplate.strategies.ConfigParserMerge
    config:
      preserve_sections: []
      preserve_keys:
      - sections: ^pytest$
        keys: ^python_files$

  .isort.cfg:
    strategy: scaraplate.strategies.ConfigParserMerge
    config:
      preserve_sections: []
      preserve_keys:
      - sections: ^settings$
        keys: ^known_third_party$
class Schema

Allowed params:

  • preserve_keys (required) – the list of config keys which should be preserved from the target file. Values schema:

    • sections (required) – a PCRE pattern matching sections containing the keys to preserve.
    • keys (required) – a PCRE pattern matching keys in the matched sections.
  • preserve_sections (required) – the list of config sections which should be preserved from the target file. If the matching section exists in the template, it would be fully overwritten. Values schema:

    • sections (required) – a PCRE pattern matching sections which should be preserved from the target.
    • excluded_keys [None] – a PCRE pattern matching the keys which should not be overwritten in the template when preserving the section.
class scaraplate.strategies.SetupCfgMerge

Bases: scaraplate.strategies.ConfigParserMerge

A strategy which merges the Python’s setup.cfg file.

Based on the ConfigParserMerge strategy, additionally containing a merge_requirements config option for merging the lists of Python requirements between the files.

Sample scaraplate.yaml excerpt:

strategies_mapping:
  setup.cfg:
    strategy: scaraplate.strategies.SetupCfgMerge
    config:
      merge_requirements:
      - sections: ^options$
        keys: ^install_requires$
      - sections: ^options\.extras_require$
        keys: ^develop$
      preserve_keys:
      - sections: ^tool:pytest$
        keys: ^testpaths$
      - sections: ^build$
        keys: ^executable$
      preserve_sections:
      - sections: ^mypy-
      - sections: ^options\.data_files$
      - sections: ^options\.entry_points$
      - sections: ^options\.extras_require$
class Schema

Allowed params:

  • preserve_keys (required) – the list of config keys which should be preserved from the target file. Values schema:

    • sections (required) – a PCRE pattern matching sections containing the keys to preserve.
    • keys (required) – a PCRE pattern matching keys in the matched sections.
  • preserve_sections (required) – the list of config sections which should be preserved from the target file. If the matching section exists in the template, it would be fully overwritten. Values schema:

    • sections (required) – a PCRE pattern matching sections which should be preserved from the target.
    • excluded_keys [None] – a PCRE pattern matching the keys which should not be overwritten in the template when preserving the section.
  • merge_requirements (required) – the list of config keys containing the lists of Python requirements which should be merged together. Values schema:

    • sections (required) – a PCRE pattern matching sections containing the keys with requirements.
    • keys (required) – a PCRE pattern matching keys in the matched sections.