pyscaffold package

Submodules

pyscaffold.actions module

Default PyScaffold’s actions and functions to manipulate them.

When generating a project, PyScaffold uses a pipeline of functions (each function will receive as arguments the values returned by the previous function). These functions have an specific purpose and are called actions. Please follow the Action signature when developing your own action.

Note

Some actions are more complex and are placed in dedicated modules together with other auxiliary functions, see pyscaffold.structure, pyscaffold.update.

pyscaffold.actions.Action

Signature of a PyScaffold action, both arguments should be treated as immutable, but a copy of the arguments, modified by the extension might be returned:

Callable[[Structure, ScaffoldOpts], Tuple[Structure, ScaffoldOpts]]

alias of Callable[[Dict[str, Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template, Tuple[Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template], Callable[[pathlib.Path, Optional[str], Dict[str, Any]], Optional[pathlib.Path]]], dict]], Dict[str, Any]], Tuple[Dict[str, Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template, Tuple[Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template], Callable[[pathlib.Path, Optional[str], Dict[str, Any]], Optional[pathlib.Path]]], dict]], Dict[str, Any]]]

pyscaffold.actions.ActionParams

Both argument and return type of an action (struct, opts), so a sequence of actions work in pipeline:

Tuple[Structure, ScaffoldOpts]

When actions run, they can return an updated copy of Structure and ScaffoldOpts.

alias of Tuple[Dict[str, Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template, Tuple[Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template], Callable[[pathlib.Path, Optional[str], Dict[str, Any]], Optional[pathlib.Path]]], dict]], Dict[str, Any]]

pyscaffold.actions.DEFAULT: List[Callable[[Dict[str, Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template, Tuple[Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template], Callable[[pathlib.Path, Optional[str], Dict[str, Any]], Optional[pathlib.Path]]], dict]], Dict[str, Any]], Tuple[Dict[str, Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template, Tuple[Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template], Callable[[pathlib.Path, Optional[str], Dict[str, Any]], Optional[pathlib.Path]]], dict]], Dict[str, Any]]]] = [<function get_default_options>, <function verify_options_consistency>, <function define_structure>, <function verify_project_dir>, <function version_migration>, <function create_structure>, <function init_git>, <function report_done>]

Default list of actions forming the main pipeline executed by PyScaffold

pyscaffold.actions.ScaffoldOpts

Dictionary with PyScaffold’s options, see pyscaffold.api.create_project.

alias of Dict[str, Any]

pyscaffold.actions.discover(extensions)[source]

Retrieve the action list.

This is done by concatenating the default list with the one generated after activating the extensions.

Parameters

extensions – list of functions responsible for activating the extensions.

pyscaffold.actions.get_default_options(struct, opts)[source]

Compute all the options that can be automatically derived.

This function uses all the available information to generate sensible defaults. Several options that can be derived are computed when possible.

Parameters
  • struct – project representation as (possibly) nested dict.

  • opts – given options, see create_project for an extensive list.

Returns

project representation and options with default values set

Return type

ActionParams

Raises
  • DirectoryDoesNotExist – when PyScaffold is told to update an nonexistent directory

  • GitNotInstalled – when git command is not available

  • GitNotConfigured – when git does not know user information

Note

This function uses git to determine some options, such as author name and email.

pyscaffold.actions.init_git(struct, opts)[source]

Add revision control to the generated files.

Parameters
  • struct – project representation as (possibly) nested dict.

  • opts – given options, see create_project for an extensive list.

Returns

Updated project representation and options

pyscaffold.actions.invoke(struct_and_opts, action)[source]

Invoke action with proper logging.

Parameters
  • struct_and_opts – PyScaffold’s arguments for actions

  • action – to be invoked

Returns

updated project representation and options

Return type

ActionParams

pyscaffold.actions.register(actions, action, before=None, after=None)[source]

Register a new action to be performed during scaffold.

Parameters
  • actions (List[Action]) – previous action list.

  • action (Action) –

    function with two arguments: the first one is a (nested) dict representing the file structure of the project and the second is a dict with scaffold options. This function MUST return a tuple with two elements similar to its arguments. Example:

    def do_nothing(struct, opts):
        return (struct, opts)
    

  • **kwargs (dict) –

    keyword arguments make it possible to choose a specific order when executing actions: when before or after keywords are provided, the argument value is used as a reference position for the new action. Example:

    register(actions, do_nothing,
             after='create_structure')
        # Look for the first action with a name
        # `create_structure` and inserts `do_nothing` after it.
        # If more than one registered action is named
        # `create_structure`, the first one is selected.
    
    register(
        actions, do_nothing,
        before='pyscaffold.structure:create_structure')
        # Similar to the previous example, but the probability
        # of name conflict is decreased by including the module
        # name.
    

    When no keyword argument is provided, the default execution order specifies that the action will be performed after the project structure is defined, but before it is written to the disk. Example:

    register(actions, do_nothing)
        # The action will take place after
        # `pyscaffold.structure:define_structure`
    

Returns

modified action list.

Return type

List[Action]

pyscaffold.actions.report_done(struct, opts)[source]

Just inform the user PyScaffold is done

pyscaffold.actions.unregister(actions, reference)[source]

Prevent a specific action to be executed during scaffold.

Parameters
  • actions (List[Action]) – previous action list.

  • reference (str) –

    action identifier. Similarly to the keyword arguments of register it can assume two formats:

    • the name of the function alone,

    • the name of the module followed by : and the name of the function

Returns

modified action list.

Return type

List[Action]

pyscaffold.actions.verify_options_consistency(struct, opts)[source]

Perform some sanity checks about the given options.

Parameters
  • struct – project representation as (possibly) nested dict.

  • opts – given options, see create_project for an extensive list.

Returns

Updated project representation and options

pyscaffold.actions.verify_project_dir(struct, opts)[source]

Check if PyScaffold can materialize the project dir structure.

Parameters
  • struct – project representation as (possibly) nested dict.

  • opts – given options, see create_project for an extensive list.

Returns

Updated project representation and options

pyscaffold.api module

External API for accessing PyScaffold programmatically via Python.

pyscaffold.api.DEFAULT_OPTIONS = {'config_files': [], 'description': 'Add a short description here!', 'extensions': [], 'force': False, 'license': 'MIT', 'update': False, 'url': 'https://github.com/pyscaffold/pyscaffold/', 'version': 'unknown'}

Default values for PyScaffold’s options.

Options that can be derived from the values of other options (e.g. package can be derived from project_path when not explicitly passed) are computed in pyscaffold.actions.get_default_options.

When config_files is empty, a default value is computed dynamically by pyscaffold.info.config_file before the start of PyScaffold’s action pipeline.

Warning

Default values might be dynamically overwritten by config_files or, during updates, existing setup.cfg.

pyscaffold.api.NO_CONFIG = <ConfigFiles.NO_CONFIG: 1>

This constant is used to tell PyScaffold to not load any extra configuration file, not even the default ones Usage:

create_project(opts, config_files=NO_CONFIG)

Please notice that the setup.cfg file inside an project being updated will still be considered.

pyscaffold.api.bootstrap_options(opts=None, **kwargs)[source]

Internal API: augment the given options with minimal defaults and existing configurations saved in files (e.g. setup.cfg)

See list of arguments in create_project. Returns a dictionary of options.

Warning

This function is not part of the public Python API of PyScaffold, and therefore might change even in minor/patch releases (not bounded to semantic versioning).

Note

This function does not replace the pyscaffold.actions.get_default_options action. Instead it is needed to ensure that action works correctly.

pyscaffold.api.create_project(opts=None, **kwargs)[source]

Create the project’s directory structure

Parameters
  • opts (dict) – options of the project

  • **kwargs – extra options, passed as keyword arguments

Returns

a tuple of struct and opts dictionary

Return type

tuple

Valid options include:

Project Information
Naming
  • name (str): as in pip install or in PyPI

  • package (str): Python identifier as in import (without namespace)

Package Information
  • author (str)

  • email (str)

  • release_date (str)

  • year (str)

  • title (str)

  • description (str)

  • url (str)

  • classifiers (str)

  • requirements (list)

PyScaffold Control
  • update (bool)

  • force (bool)

  • pretend (bool)

  • extensions (list)

  • config_files (list or NO_CONFIG)

Some of these options are equivalent to the command line options, others are used for creating the basic python package meta information, but the ones in the “PyScaffold Control” group affects how the “scaffolding” behaves.

When the force flag is True, existing files will be overwritten. When the update flag is True, PyScaffold will consider that some files can be updated (usually the packaging boilerplate), but will keep others intact. When the pretend flag is True, the project will not be created/updated, but the expected outcome will be logged.

The extensions list may contain any object that follows the extension API. Note that some PyScaffold features, such as cirrus, tox and pre-commit support, are implemented as built-in extensions. In order to use these features it is necessary to include the respective objects in the extension list. All built-in extensions are accessible via pyscaffold.extensions submodule.

Finally, when setup.cfg-like files are added to the config_files list, PyScaffold will read it’s options from there in addition to the ones already passed. If the list is empty, the default configuration file is used. To avoid reading any existing configuration, please pass config_file=NO_CONFIG. See https://pyscaffold.org/en/latest/configuration.html for more details.

Note that extensions may define extra options. For example, the cookiecutter extension define a cookiecutter option that should be the address to the git repository used as template and the namespace extension define a namespace option with the name of a PEP 420 compatible (and possibly nested) namespace.

pyscaffold.cli module

Command-Line-Interface of PyScaffold

pyscaffold.cli.add_default_args(parser)[source]

Add the default options and arguments to the CLI parser.

pyscaffold.cli.add_extension_args(parser)[source]

Add options and arguments defined by extensions to the CLI parser.

Add options that control verbosity/logger level

pyscaffold.cli.get_log_level(args=None)[source]

Get the configured log level directly by parsing CLI options from args or obj:sys.argv.

Useful when the CLI crashes before applying the changes to the logger.

pyscaffold.cli.list_actions(opts)[source]

Do not create a project, just list actions considering extensions

Parameters

opts (dict) – command line options as dictionary

pyscaffold.cli.main(args)[source]

Main entry point for external applications

Parameters

args – command line arguments

pyscaffold.cli.parse_args(args)[source]

Parse command line parameters respecting extensions

Parameters

args – command line parameters as list of strings

Returns

command line parameters

Return type

dict

pyscaffold.cli.run(args=None)[source]

Entry point for console script

pyscaffold.cli.run_scaffold(opts)[source]

Actually scaffold the project, calling the python API

Parameters

opts (dict) – command line options as dictionary

pyscaffold.dependencies module

Internal library for manipulating package dependencies and requirements.

pyscaffold.dependencies.BUILD = ('setuptools_scm>=5', 'wheel')

Dependencies that will be required to build the created project

pyscaffold.dependencies.ISOLATED = ('setuptools>=46.1.0', 'setuptools_scm[toml]>=5', 'wheel')

Dependencies for isolated builds (PEP517/518). - setuptools min version might be slightly higher then the version required at runtime. - setuptools_scm requires an optional dependency to work with pyproject.toml

pyscaffold.dependencies.REQ_SPLITTER = re.compile(';(?!\\s*(python|platform|implementation|os|sys)_)', re.MULTILINE)

Regex to split requirements that considers both setup.cfg specs and PEP 508 (in a good enough simplified fashion).

pyscaffold.dependencies.RUNTIME = ('importlib-metadata; python_version<"3.8"',)

Dependencies that will be required at runtime by the created project

pyscaffold.dependencies.add(requirements, to_add=('setuptools_scm>=5', 'wheel'))[source]

Given a sequence of individual requirement strings, add to_add to it. By default adds BUILD if to_add is not given.

pyscaffold.dependencies.deduplicate(requirements)[source]

Given a sequence of individual requirement strings, e.g. ["appdirs>=1.4.4", "packaging>20.0"], remove the duplicated packages. If a package is duplicated, the last occurrence stays.

pyscaffold.dependencies.remove(requirements, to_remove)[source]

Given a list of individual requirement strings, e.g. ["appdirs>=1.4.4", "packaging>20.0"], remove the requirements in to_remove.

pyscaffold.dependencies.split(requirements)[source]

Split a combined requirement string (such as the values for setup_requires and install_requires in setup.cfg) into a list of individual requirement strings, that can be used in is_included, get_requirements_str, remove, etc…

pyscaffold.exceptions module

Functions for exception manipulation + custom exceptions used by PyScaffold to identify common deviations from the expected behavior.

exception pyscaffold.exceptions.ActionNotFound(name, *args, **kwargs)[source]

Bases: KeyError

Impossible to find the required action.

exception pyscaffold.exceptions.DirectoryAlreadyExists[source]

Bases: RuntimeError

The project directory already exists, but no update or force option was used.

exception pyscaffold.exceptions.DirectoryDoesNotExist[source]

Bases: RuntimeError

No directory was found to be updated.

exception pyscaffold.exceptions.ErrorLoadingExtension(extension='', entry_point=None)[source]

Bases: RuntimeError

There was an error loading ‘{extension}’. Please make sure you have installed a version of the extension that is compatible with PyScaffold {version}. You can also try unininstalling it.

exception pyscaffold.exceptions.GitDirtyWorkspace(message="Your working tree is dirty. Commit your changes first or use '--force'.", *args, **kwargs)[source]

Bases: RuntimeError

Workspace of git is empty.

DEFAULT_MESSAGE = "Your working tree is dirty. Commit your changes first or use '--force'."
exception pyscaffold.exceptions.GitNotConfigured(message='Make sure git is configured. Run:\n  git config --global user.email "you@example.com"\n  git config --global user.name "Your Name"\nto set your account\'s default identity.', *args, **kwargs)[source]

Bases: RuntimeError

PyScaffold tries to read user.name and user.email from git config.

DEFAULT_MESSAGE = 'Make sure git is configured. Run:\n  git config --global user.email "you@example.com"\n  git config --global user.name "Your Name"\nto set your account\'s default identity.'
exception pyscaffold.exceptions.GitNotInstalled(message='Make sure git is installed and working.', *args, **kwargs)[source]

Bases: RuntimeError

PyScaffold requires git to run.

DEFAULT_MESSAGE = 'Make sure git is installed and working.'
exception pyscaffold.exceptions.ImpossibleToFindConfigDir(message=None, *args, **kwargs)[source]

Bases: RuntimeError

An expected error occurred when trying to find the config dir.

This might be related to not being able to read the $HOME env var in Unix systems, or %USERPROFILE% in Windows, or even the username.

exception pyscaffold.exceptions.InvalidIdentifier[source]

Bases: RuntimeError

Python requires a specific format for its identifiers.

https://docs.python.org/3.6/reference/lexical_analysis.html#identifiers

exception pyscaffold.exceptions.NoPyScaffoldProject(message='Could not update project. Was it generated with PyScaffold?', *args, **kwargs)[source]

Bases: RuntimeError

PyScaffold cannot update a project that it hasn’t generated

DEFAULT_MESSAGE = 'Could not update project. Was it generated with PyScaffold?'
exception pyscaffold.exceptions.PyScaffoldTooOld(message='setup.cfg has no section [pyscaffold]! Are you trying to update a pre 3.0 version?', *args, **kwargs)[source]

Bases: RuntimeError

PyScaffold cannot update a pre 3.0 version

DEFAULT_MESSAGE = 'setup.cfg has no section [pyscaffold]! Are you trying to update a pre 3.0 version?'
exception pyscaffold.exceptions.ShellCommandException[source]

Bases: RuntimeError

Outputs proper logging when a ShellCommand fails

pyscaffold.exceptions.exceptions2exit(exception_list)[source]

Decorator to convert given exceptions to exit messages

This avoids displaying nasty stack traces to end-users

Parameters

[Exception] (exception_list) – list of exceptions to convert

pyscaffold.file_system module

Internal library that encapsulate file system manipulation. Examples include: creating/removing files and directories, changing permissions, etc.

Functions in this library usually extend the behaviour of Python’s standard lib by providing proper error handling or adequate logging/control flow in the context of PyScaffold (an example of adequate control flow logic is dealing with the pretend flag).

pyscaffold.file_system.ERROR_INVALID_NAME = 123

Windows-specific error code indicating an invalid pathname.

pyscaffold.file_system.chdir(path, **kwargs)[source]

Contextmanager to change into a directory

Parameters

path – path to change current working directory to

Keyword Arguments
  • log (bool) – log activity when true. Default: False.

  • pretend (bool) – skip execution (but log) when pretending. Default False.

pyscaffold.file_system.chmod(path, mode, pretend=False)[source]

Change the permissions of file in the given path.

This function reports the operation in the logs.

Parameters
  • path – path in the file system whose permissions will be changed

  • mode – new permissions, should be a combination of :obj`stat.S_* <stat.S_IXUSR>` (see os.chmod).

  • pretend (bool) – false by default. File is not changed when pretending, but operation is logged.

pyscaffold.file_system.create_directory(path, update=False, pretend=False)[source]

Create a directory in the given path.

This function reports the operation in the logs.

Parameters
  • path – path in the file system where contents will be written.

  • update (bool) – false by default. A OSError can be raised when update is false and the directory already exists.

  • pretend (bool) – false by default. Directory is not created when pretending, but operation is logged.

pyscaffold.file_system.create_file(path, content, pretend=False, encoding='utf-8')[source]

Create a file in the given path.

This function reports the operation in the logs.

Parameters
  • path – path in the file system where contents will be written.

  • content – what will be written.

  • pretend (bool) – false by default. File is not written when pretending, but operation is logged.

Returns

given path

Return type

Path

pyscaffold.file_system.is_pathname_valid(pathname)[source]

Check if a pathname is valid

Code by Cecil Curry from StackOverflow

Parameters

pathname (str) – string to validate

Returns

True if the passed pathname is a valid pathname for the current OS; False otherwise.

pyscaffold.file_system.localize_path(path_string)[source]

Localize path for Windows, Unix, i.e. / or

Parameters

path_string (str) – path using /

Returns

path depending on OS

Return type

str

pyscaffold.file_system.move(*src, target, **kwargs)[source]

Move files or directories to (into) a new location

Parameters

*src (PathLike) – one or more files/directories to be moved

Keyword Arguments
  • target (PathLike) – if target is a directory, src will be moved inside it. Otherwise, it will be the new path (note that it may be overwritten)

  • log (bool) – log activity when true. Default: False.

  • pretend (bool) – skip execution (but log) when pretending. Default False.

pyscaffold.file_system.on_ro_error(func, path, exc_info)[source]

Error handler for shutil.rmtree.

If the error is due to an access error (read only file) it attempts to add write permission and then retries.

If the error is for another reason it re-raises the error.

Usage : shutil.rmtree(path, onerror=onerror)

Parameters
  • func (callable) – function which raised the exception

  • path (str) – path passed to func

  • exc_info (tuple of str) – exception info returned by sys.exc_info()

pyscaffold.file_system.rm_rf(path, pretend=False)[source]

Remove path by all means like rm -rf in Linux

pyscaffold.file_system.tmpfile(**kwargs)[source]

Context manager that yields a temporary Path

pyscaffold.identification module

Internal library for manipulating, creating and dealing with names, or more generally identifiers.

pyscaffold.identification.dasherize(word)[source]

Replace underscores with dashes in the string.

Example:

>>> dasherize("foo_bar")
"foo-bar"
Parameters

word (str) – input word

Returns

input word with underscores replaced by dashes

pyscaffold.identification.deterministic_name(obj)[source]

Private API that returns an string that can be used to deterministically deduplicate and sort sequences of objects.

pyscaffold.identification.deterministic_sort(sequence)[source]

Private API that order a sequence of objects lexicographically (by deterministic_name), removing duplicates, which is needed for determinism.

The main purpose of this function is to deterministically sort a sequence of PyScaffold extensions (it will also sort internal extensions before external: “pyscaffold.*” < “pyscaffoldext.*”).

pyscaffold.identification.get_id(function)[source]

Given a function, calculate its identifier.

A identifier is a string in the format <module name>:<function name>, similarly to the convention used for setuptools entry points.

Note

This function does not return a Python 3 __qualname__ equivalent. If the function is nested inside another function or class, the parent name is ignored.

Parameters

function (callable) – function object

pyscaffold.identification.is_valid_identifier(string)[source]

Check if string is a valid package name

Parameters

string – package name

Returns

True if string is valid package name else False

pyscaffold.identification.levenshtein(s1, s2)[source]

Calculate the Levenshtein distance between two strings

Parameters
  • s1 – first string

  • s2 – second string

Returns

Distance between s1 and s2

pyscaffold.identification.make_valid_identifier(string)[source]

Try to make a valid package name identifier from a string

Parameters

string – invalid package name

Returns

Valid package name as string or RuntimeError

Raises

InvalidIdentifier – raised if identifier can not be converted

pyscaffold.identification.underscore(word)[source]

Convert CamelCasedStrings or dasherized-strings into underscore_strings.

Example:

>>> underscore("FooBar-foo")
"foo_bar_foo"

pyscaffold.info module

Provide general information about the system, user and the package itself.

pyscaffold.info.CONFIG_FILE = 'default.cfg'

PyScaffold’s own config file name

class pyscaffold.info.GitEnv(value)[source]

Bases: enum.Enum

An enumeration.

author_date = 'GIT_AUTHOR_DATE'
author_email = 'GIT_AUTHOR_EMAIL'
author_name = 'GIT_AUTHOR_NAME'
committer_date = 'GIT_COMMITTER_DATE'
committer_email = 'GIT_COMMITTER_EMAIL'
committer_name = 'GIT_COMMITTER_NAME'
pyscaffold.info.RAISE_EXCEPTION = <default.RAISE_EXCEPTION: 1>

When no default value is passed, an exception should be raised

pyscaffold.info.best_fit_license(txt)[source]

Finds proper license name for the license defined in txt

pyscaffold.info.check_git()[source]

Checks for git and raises appropriate exception if not

Raises
  • GitNotInstalled – when git command is not available

  • GitNotConfigured – when git does not know user information

pyscaffold.info.config_dir(prog='pyscaffold', org=None, default=<default.RAISE_EXCEPTION: 1>)[source]

Finds the correct place where to read/write configurations for the given app.

Parameters
  • prog – program name (defaults to pyscaffold)

  • org – organisation/author name (defaults to the same as prog)

  • default – default value to return if an exception was raise while trying to find the config dir. If no default value is passed, an ImpossibleToFindConfigDir execution is raised.

Please notice even if the directory doesn’t exist, if its path is possible to calculate, this function will return a Path object (that can be used to create the directory)

Returns

Location somewhere in the user’s home directory where to put the configs.

pyscaffold.info.config_file(name='default.cfg', prog='pyscaffold', org=None, default=<default.RAISE_EXCEPTION: 1>)[source]

Finds a file inside config_dir.

Parameters

name – file you are looking for

The other args are the same as in config_dir and have the same meaning.

Returns

Location of the config file or default if an error happened.

pyscaffold.info.email()[source]

Retrieve the user’s email

pyscaffold.info.get_curr_version(project_path)[source]

Retrieves the PyScaffold version that put up the scaffold

Parameters

project_path – path to project

Returns

version specifier

Return type

Version

pyscaffold.info.is_git_configured()[source]

Check if user.name and user.email is set globally in git

Check first git environment variables, then config settings. This will also return false if git is not available at all.

Returns

True if it is set globally, False otherwise

pyscaffold.info.is_git_installed()[source]

Check if git is installed

pyscaffold.info.is_git_workspace_clean(path)[source]

Checks if git workspace is clean

Parameters

path

path to git repository

Raises:

GitNotInstalled: when git command is not available GitNotConfigured: when git does not know user information

pyscaffold.info.project(opts, config_path=None, config_file=None)[source]

Update user options with the options of an existing config file

Parameters
  • opts – options of the project

  • config_path – path where config file can be found (default: opts["project_path"])

  • config_file – if config_path is a directory, name of the config file, relative to it (default: setup.cfg)

Returns

Options with updated values

Raises
  • PyScaffoldTooOld – when PyScaffold is to old to update from

  • NoPyScaffoldProject – when project was not generated with PyScaffold

pyscaffold.info.read_pyproject(path, filename='pyproject.toml')[source]

Reads-in a configuration file that follows a pyproject.toml format.

Parameters
  • path – path where to find the config file

  • filename – if path is a directory, name will be considered a file relative to path to read (default: setup.cfg)

Returns

Object that can be used to read/edit configuration parameters.

pyscaffold.info.read_setupcfg(path, filename='setup.cfg')[source]

Reads-in a configuration file that follows a setup.cfg format. Useful for retrieving stored information (e.g. during updates)

Parameters
  • path – path where to find the config file

  • filename – if path is a directory, name will be considered a file relative to path to read (default: setup.cfg)

Returns

Object that can be used to read/edit configuration parameters.

pyscaffold.info.username()[source]

Retrieve the user’s name

pyscaffold.log module

Custom logging infrastructure to provide execution information for the user.

class pyscaffold.log.ColoredReportFormatter(fmt=None, datefmt=None, style='%', validate=True)[source]

Bases: pyscaffold.log.ReportFormatter

Format logs with ANSI colors.

ACTIVITY_STYLES: DefaultDict[str, Sequence[str]] = {'create': ('green', 'bold'), 'delete': ('red', 'bold'), 'invoke': ('bold',), 'move': ('green', 'bold'), 'remove': ('red', 'bold'), 'run': ('magenta', 'bold'), 'skip': ('yellow', 'bold')}
CONTEXT_PREFIX = '\x1b[35m\x1b[1mfrom\x1b[0m'
LOG_STYLES: DefaultDict[str, Sequence[str]] = {'critical': ('red', 'bold'), 'debug': ('green',), 'error': ('red',), 'info': ('blue',), 'warning': ('yellow',)}
SUBJECT_STYLES: DefaultDict[str, Sequence[str]] = {'invoke': ('blue',)}
TARGET_PREFIX = '\x1b[35m\x1b[1mto\x1b[0m'
format_activity(activity)[source]

Format the activity keyword.

format_default(record)[source]

Format default log messages.

format_subject(subject, activity=None)[source]

Format the subject of the activity.

pyscaffold.log.DEFAULT_LOGGER = 'pyscaffold.log'

Name of PyScaffold’s default logger (it can be used with logging.getLogger)

class pyscaffold.log.ReportFormatter(fmt=None, datefmt=None, style='%', validate=True)[source]

Bases: logging.Formatter

Formatter that understands custom fields in the log record.

ACTIVITY_MAXLEN = 12
CONTEXT_PREFIX = 'from'
SPACING = '  '
TARGET_PREFIX = 'to'
create_padding(activity)[source]

Create the appropriate padding in order to align activities.

format(record)[source]

Compose message when a record with report information is given.

format_activity(activity)[source]

Format the activity keyword.

format_context(context, _activity=None)[source]

Format extra information about the activity context.

format_default(record)[source]

Format default log messages.

format_path(path)[source]

Simplify paths to avoid wasting space in terminal.

format_report(record)[source]

Compose message when a custom record is given.

format_subject(subject, _activity=None)[source]

Format the subject of the activity.

format_target(target, _activity=None)[source]

Format extra information about the activity target.

class pyscaffold.log.ReportLogger(logger=None, handler=None, formatter=None, extra=None, propagate=False)[source]

Bases: logging.LoggerAdapter

Suitable wrapper for PyScaffold CLI interactive execution reports.

Parameters
nesting

current nesting level of the report.

Type

int

copy()[source]

Produce a copy of the wrapped logger.

Sometimes, it is better to make a copy of th report logger to keep indentation consistent.

property formatter

Formatter configured in the default handler

property handler

Stream handler configured for providing user feedback in PyScaffold CLI

indent(count=1)[source]

Temporarily adjust padding while executing a context.

Example

from pyscaffold.log import logger

logger.report("invoke", "custom_action")
with logger.indent():
    logger.report("create", "some/file/path")

# Expected logs:
# --------------------------------------
#       invoke  custom_action
#       create    some/file/path
# --------------------------------------
# Note how the spacing between activity and subject in the
# second entry is greater than the equivalent in the first one.

Note

This method is not thread-safe and should be used with care.

property level

Effective level of the logger

process(msg, kwargs)[source]

Method overridden to augment LogRecord with the nesting attribute

property propagate

Whether or not to propagate messages in the logging hierarchy, See logging.Logger.propagate.

reconfigure(opts=None, **kwargs)[source]

Reconfigure some aspects of the logger object.

Parameters

opts (dict) – dict with the same elements as the keyword arguments bellow

Keyword Arguments
  • log_level – One of the log levels specified in the logging module.

  • use_colors – automatically set a colored formatter to the logger if ANSI codes support is detected. (Defaults to True).

Additional keyword arguments will be ignored.

report(activity, subject, context=None, target=None, nesting=None, level=20)[source]

Log that an activity has occurred during scaffold.

Parameters
  • activity (str) – usually a verb or command, e.g. create, invoke, run, chdir

  • subject (str or os.PathLike) – usually a path in the file system or an action identifier.

  • context (str or os.PathLike) – path where the activity take place.

  • target (str or os.PathLike) – path affected by the activity

  • nesting (int) – optional nesting level. By default it is calculated from the activity name.

  • level (int) – log level. Defaults to logging.INFO. See logging for more information.

Notes

This method creates a custom log record, with additional fields: activity, subject, context, target and nesting, but an empty msg field. The ReportFormatter creates the log message from the other fields.

Often target and context complement the logs when subject does not hold all the necessary information. For example:

logger.report('copy', 'my/file', target='my/awesome/path')
logger.report('run', 'command', context='current/working/dir')
property wrapped

Underlying logger object

pyscaffold.log.logger = <ReportLogger pyscaffold.log (WARNING)>

Default logger configured for PyScaffold.

pyscaffold.operations module

Collection of functions responsible for the “reification” of the file representation in the project structure into a file written to the disk.

A function that “reifies” a file (manifests the programmatic representation of the file in the project structure dictionary to the disk) is called here a file operation or simply a file op. Actually file ops don’t even need to be functions strictly speaking, they can be any callable object. The only restriction is that file ops MUST respect the FileOp signature. The create function is a good example of how to write a new file op.

A function (or callable) that modifies the behaviour of an existing file op, by wrapping it with another function/callable is called here modifier. Modifiers work similarly to Python decorators and allow extending/refining the existing file ops. A modifier should receive a file op as argument and return another file op. no_overwrite and skip_on_update are good examples on how to write new file op modifiers.

While modifiers don’t have a specific signature (the number of parameters might vary, but they always return a single file op value), the following conventions SHOULD be observed when creating new modifiers:

  • Modifiers should accept at least one argument: the file op they modify (you don’t have to be very creative, go ahead and name this parameter file_op, it is a good convention). This parameter should be made optional (the default value of create usually works, unless there is a better default value for the main use case of the modifier).

  • Modifiers can accept arguments other then the file op they modify. These arguments should precede the file op in the list of arguments (the file op should be the last argument, which interoperates well with partial).

  • When writing a modifier that happens to be a function (instead of a callable class), please name the inner function with the same name of the modifier but preceded by an _ (underscore) char. This allows better inspection/debugging.

Changed in version 4.0: Previously, file operations where simply indicated as a numeric flag (the members of pyscaffold.structure.FileOp) in the project structure. Starting from PyScaffold 4, file operation are functions with signature given by FileOp.

pyscaffold.operations.FileContents

When the file content is None, the file should not be written to disk (empty files are represented by an empty string "" as content).

alias of Optional[str]

pyscaffold.operations.FileOp

Signature of functions considered file operations:

Callable[[Path, FileContents, ScaffoldOpts], Union[Path, None]]
Parameters
  • path (pathlib.Path) – file path potentially to be written to/changed in the disk.

  • contents (FileContents) – usually a string that represents a text content of the file. None indicates the file should not be written.

  • opts (ScaffoldOpts) – a dict with PyScaffold’s options.

Returns

If the file is written (or more generally changed, such as new access permissions), by convention they should return the file path. If no file was touched, None should be returned. Please notice a FileOp might return None if a pre-existing file in the disk is not modified.

Note

A FileOp usually has side effects (e.g. write a file to the disk), see FileFileContents and ScaffoldOpts for other conventions.

alias of Callable[[pathlib.Path, Optional[str], Dict[str, Any]], Optional[pathlib.Path]]

pyscaffold.operations.ScaffoldOpts

Dictionary with PyScaffold’s options, see pyscaffold.api.create_project. Should be treated as immutable (if required, copy before changing).

Please notice some behaviours given by the options SHOULD be observed. For example, files should be overwritten when the force option is True. Similarly when pretend is True, no operation should be really performed, but any action should be logged as if realized.

alias of Dict[str, Any]

pyscaffold.operations.add_permissions(permissions, file_op=<function create>)[source]

File op modifier. Returns a FileOp that will add access permissions to the file (on top of the ones given by default by the OS).

Parameters
  • permissions (int) –

    permissions to be added to file:

    updated file mode = old mode | permissions  (bitwise OR)
    

    Preferably the values should be a combination of stat.S_* values (see os.chmod).

  • file_op – a FileOp that will be “decorated”. If the file exists in disk after file_op is called (either created or pre-existing), permissions will be added to it. Default: create.

Warning

This is an experimental file op and might be subject to incompatible changes (or complete removal) even in minor/patch releases.

Note

File access permissions work in a completely different way depending on the operating system. This file op is straightforward on POSIX systems, but a bit tricky on Windows. It should be safe for desirable but not required effects (e.g. making a file with a shebang executable, but that can also run via python FILE), or ensuring files are readable/writable.

pyscaffold.operations.create(path, contents, opts)[source]

Default FileOp: always create/write the file even during (forced) updates.

pyscaffold.operations.no_overwrite(file_op=<function create>)[source]

File op modifier. Returns a FileOp that does not overwrite an existing file during update (still created if not exists).

Parameters

file_op – a FileOp that will be “decorated”, i.e. will be called if the no_overwrite conditions are met. Default: create.

pyscaffold.operations.remove(path, _content, opts)[source]

Remove the file if it exists in the disk

pyscaffold.operations.skip_on_update(file_op=<function create>)[source]

File op modifier. Returns a FileOp that will skip the file during a project update (the file will just be created for brand new projects).

Parameters

file_op – a FileOp that will be “decorated”, i.e. will be called if the skip_on_update conditions are met. Default: create.

pyscaffold.repo module

Functionality for working with a git repository

pyscaffold.repo.add_tag(project, tag_name, message=None, **kwargs)[source]

Add an (annotated) tag to the git repository.

Parameters
  • project – path to the project

  • tag_name – name of the tag

  • message – optional tag message

Additional keyword arguments are passed to the git callable object.

pyscaffold.repo.get_git_root(default=None)[source]

Return the path to the top-level of the git repository or default.

Parameters

default – if no git root is found, default is returned

Returns

top-level path or default

Return type

str

pyscaffold.repo.git_tree_add(struct, prefix='', **kwargs)[source]

Adds recursively a directory structure to git

Parameters
  • struct – directory structure as dictionary of dictionaries

  • prefix – prefix for the given directory structure

Additional keyword arguments are passed to the git callable object.

pyscaffold.repo.init_commit_repo(project, struct, **kwargs)[source]

Initialize a git repository

Parameters
  • project – path to the project

  • struct – directory structure as dictionary of dictionaries

Additional keyword arguments are passed to the git callable object.

pyscaffold.repo.is_git_repo(folder)[source]

Check if a folder is a git repository

pyscaffold.shell module

Shell commands like git, django-admin etc.

pyscaffold.shell.EDITORS = ('sensible-editor', 'nvim', 'vim', 'nano', 'subl', 'code', 'notepad', 'vi')

Programs to be tried (in sequence) when calling edit and get_editor in the case the environment variables EDITOR and VISUAL are not set.

class pyscaffold.shell.ShellCommand(command, shell=True, cwd=None)[source]

Bases: object

Shell command that can be called with flags like git(‘add’, ‘file’)

Parameters
  • command – command to handle

  • shell – run the command in the shell

  • cwd – current working dir to run the command

The produced command can be called with the following keyword arguments:

  • log (bool): log activity when true. False by default.

  • pretend (bool): skip execution (but log) when pretending. False by default.

The positional arguments are passed to the underlying shell command.

run(*args, **kwargs)[source]

Execute command with the given arguments via subprocess.run.

pyscaffold.shell.command_exists(cmd)[source]

Check if command exists

Parameters

cmd – executable name

pyscaffold.shell.edit(file, *args, **kwargs)[source]

Open a text editor and returns back a Path to file, after user editing.

pyscaffold.shell.get_command(name, prefix='/home/docs/checkouts/readthedocs.org/user_builds/pyscaffold/envs/stable', include_path=True, **kwargs)[source]

Similar to get_executable but return an instance of ShellCommand if it is there to be found. Additional kwargs will be passed to the ShellCommand constructor.

pyscaffold.shell.get_editor(**kwargs)[source]

Get an available text editor program

pyscaffold.shell.get_executable(name, prefix='/home/docs/checkouts/readthedocs.org/user_builds/pyscaffold/envs/stable', include_path=True)[source]

Find an executable in the system, if available.

Parameters
  • name – name of the executable

  • prefix – look on this directory, exclusively or in additon to $PATH depending on the value of include_path. Defaults to sys.prefix.

  • include_path – when True the functions tries to look in the entire $PATH.

pyscaffold.shell.get_git_cmd(**args)[source]

Retrieve the git shell command depending on the current platform

Parameters

**args – additional keyword arguments to ShellCommand

pyscaffold.shell.git = <pyscaffold.shell.ShellCommand object>

Command for git

pyscaffold.shell.shell_command_error2exit_decorator(func)[source]

Decorator to convert given ShellCommandException to an exit message

This avoids displaying nasty stack traces to end-users

pyscaffold.structure module

Functionality to generate and work with the directory structure of a project.

Changed in version 4.0: Callable[[dict], str] and string.Template objects can also be used as file contents. They will be called with PyScaffold’s opts (string.Template via safe_substitute)

pyscaffold.structure.AbstractContent

Recipe for obtaining file contents

Union[FileContents, Callable[[ScaffoldOpts], FileContents], Template]

alias of Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template]

pyscaffold.structure.ActionParams

See pyscaffold.actions.ActionParams

alias of Tuple[Dict[str, Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template, Tuple[Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template], Callable[[pathlib.Path, Optional[str], Dict[str, Any]], Optional[pathlib.Path]]], dict]], Dict[str, Any]]

pyscaffold.structure.Leaf

Just the content of the file OR a tuple of content + file operation

Union[AbstractContent, ResolvedLeaf]

alias of Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template, Tuple[Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template], Callable[[pathlib.Path, Optional[str], Dict[str, Any]], Optional[pathlib.Path]]]]

pyscaffold.structure.Node

Representation of a file system node in the project structure (e.g. files, directories:

Union[Leaf, Structure]

alias of Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template, Tuple[Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template], Callable[[pathlib.Path, Optional[str], Dict[str, Any]], Optional[pathlib.Path]]], dict]

pyscaffold.structure.ReifiedLeaf

Similar to ResolvedLeaf but with file contents “reified”, i.e. an actual string instead of a “lazy object” (such as a function or template).

alias of Tuple[Optional[str], Callable[[pathlib.Path, Optional[str], Dict[str, Any]], Optional[pathlib.Path]]]

pyscaffold.structure.ResolvedLeaf

Complete recipe for manipulating a file in disk (not only its contents but also the file operation:

Tuple[AbstractContent, FileOp]

alias of Tuple[Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template], Callable[[pathlib.Path, Optional[str], Dict[str, Any]], Optional[pathlib.Path]]]

pyscaffold.structure.Structure

The directory tree represented as a (possibly nested) dictionary:

Structure = Dict[str, Node]
Node = Union[Leaf, Structure]

The keys indicate the path where a file will be written, while the value indicates the content.

A nested dictionary represent a nested directory, while str, string.Template and callable values represent a file to be created. tuple values are also allowed, and in that case, the first element of the tuple represents the file content while the second element is a pyscaffold.operations (which can be seen as a recipe on how to create a file with the given content). Callable file contents are transformed into strings by calling them with PyScaffold's option dict as argument. Similarly, string.Template.safe_substitute are called with PyScaffold’s opts.

The top level keys in the dict are file/dir names relative to the project root, while keys in nested dicts are relative to the parent’s key/location.

For example:

from pyscaffold.operations import no_overwrite
struct: Structure = {
    'namespace': {
        'module.py': ('print("Hello World!")', no_overwrite())
    }
}

represents a namespace/module.py file inside the project folder with content print("Hello World!"), that will be created only if not present.

Note

None file contents are ignored and not created in disk.

alias of Dict[str, Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template, Tuple[Union[str, None, Callable[[Dict[str, Any]], Optional[str]], string.Template], Callable[[pathlib.Path, Optional[str], Dict[str, Any]], Optional[pathlib.Path]]], dict]]

pyscaffold.structure.create_structure(struct, opts, prefix=None)[source]

Manifests/reifies a directory structure in the filesystem

Parameters
  • struct – directory structure as dictionary of dictionaries

  • opts – options of the project

  • prefix – prefix path for the structure

Returns

Directory structure as dictionary of dictionaries (similar to input, but only containing the files that actually changed) and input options

Raises

TypeError – raised if content type in struct is unknown

Changed in version 4.0: Also accepts string.Template and callable objects as file contents.

pyscaffold.structure.define_structure(struct, opts)[source]

Creates the project structure as dictionary of dictionaries

Parameters
  • struct – previous directory structure (usually and empty dict)

  • opts – options of the project

Returns

Project structure and PyScaffold’s options

Changed in version 4.0: string.Template and functions added directly to the file structure.

pyscaffold.structure.ensure(struct, path, content=None, file_op=<function create>)[source]

Ensure a file exists in the representation of the project tree with the provided content. All the parent directories are automatically created.

Parameters
  • struct – project representation as (possibly) nested.

  • path

    path-like string or object relative to the structure root. See modify.

    Changed in version 4.0: The function no longer accepts a list of strings of path parts.

  • content – file text contents, None by default. The old content is preserved if None.

  • file_op – see pyscaffold.operations, create` by default.

Changed in version 4.0: Instead of a update_rule flag, the function now accepts a file_op.

Returns

Updated project tree representation

Note

Use an empty string as content to ensure a file is created empty.

pyscaffold.structure.merge(old, new)[source]

Merge two dict representations for the directory structure.

Basically a deep dictionary merge, except from the leaf update method.

Parameters
  • old – directory descriptor that takes low precedence during the merge.

  • new – directory descriptor that takes high precedence during the merge.

Changed in version 4.0: Project structure now considers everything under the top level project folder.

Returns

Resulting merged directory representation

Note

Use an empty string as content to ensure a file is created empty. (None contents will not be created).

pyscaffold.structure.modify(struct, path, modifier)[source]

Modify the contents of a file in the representation of the project tree.

If the given path does not exist, the parent directories are automatically created.

Parameters
  • struct – project representation as (possibly) nested dict. See merge.

  • path

    path-like string or object relative to the structure root. The following examples are equivalent:

    from pathlib import Path
    
    'docs/api/index.html'
    Path('docs', 'api', 'index.html')
    

    Changed in version 4.0: The function no longer accepts a list of strings of path parts.

  • modifier

    function (or callable object) that receives the old content and the old file operation as arguments and returns a tuple with the new content and new file operation. Note that, if the file does not exist in struct, None will be passed as argument. Example:

    modifier = lambda old, op: ((old or '') + 'APPENDED CONTENT'!, op)
    modifier = lambda old, op: ('PREPENDED CONTENT!' + (old or ''), op)
    

    Changed in version 4.0: modifier requires 2 arguments and now is a mandatory argument.

Changed in version 4.0: update_rule is no longer an argument. Instead the arity modifier was changed to accept 2 arguments instead of only 1. This is more suitable to handling the new pyscaffold.operations API.

Returns

Updated project tree representation

Note

Use an empty string as content to ensure a file is created empty (None contents will not be created).

pyscaffold.structure.reify_content(content, opts)[source]

Make content string (via __call__ or safe_substitute with opts if necessary)

pyscaffold.structure.reify_leaf(contents, opts)[source]

Similar to resolve_leaf but applies reify_content to the first element of the returned tuple.

pyscaffold.structure.reject(struct, path)[source]

Remove a file from the project tree representation if existent.

Parameters
  • struct – project representation as (possibly) nested.

  • path

    path-like string or object relative to the structure root. See modify.

    Changed in version 4.0: The function no longer accepts a list of strings of path parts.

Returns

Modified project tree representation

pyscaffold.structure.resolve_leaf(contents)[source]

Normalize project structure leaf to be a Tuple[AbstractContent, FileOp]

pyscaffold.termui module

Basic support for ANSI code formatting.

pyscaffold.termui.STYLES = {'black': 30, 'blue': 34, 'bold': 1, 'clear': 0, 'cyan': 36, 'green': 32, 'magenta': 35, 'on_black': 40, 'on_blue': 44, 'on_cyan': 46, 'on_green': 42, 'on_magenta': 45, 'on_red': 41, 'on_white': 47, 'on_yellow': 43, 'red': 31, 'white': 37, 'yellow': 33}

Possible styles for decorate

pyscaffold.termui.SYSTEM_SUPPORTS_COLOR = True

Handy indicator of the system capabilities (relies on colorama if available)

pyscaffold.termui.curses_available()[source]

Check if the curses package from stdlib is available.

Usually not available for windows, but its presence indicates that the terminal is capable of displaying some UI.

Returns

result of check

Return type

bool

pyscaffold.termui.decorate(msg, *styles)[source]

Use ANSI codes to format the message.

Parameters
  • msg (str) – string to be formatted

  • *styles (list) – the remaining arguments should be strings that represent the 8 basic ANSI colors. clear and bold are also supported. For background colors use on_<color>.

Returns

styled and formatted message

Return type

str

pyscaffold.termui.init_colorama()[source]

Initialize colorama if it is available.

Returns

result of check

Return type

bool

pyscaffold.termui.isatty(stream=None)[source]

Detect if the given stream/stdout is part of an interactive terminal.

Parameters

stream – optionally the stream to check

Returns

result of check

Return type

bool

pyscaffold.termui.supports_color(stream=None)[source]

Check if the stream is supposed to handle coloring.

Returns

result of check

Return type

bool

pyscaffold.toml module

Thin wrapper around the dependency so we can maintain some stability while being able to swap implementations (e.g. replace tomlkit with toml).

Despite being used in pep517, toml offers limited support for comments, so we prefer tomlkit.

pyscaffold.toml.TOMLMapping

Abstraction on the value returned by loads.

This kind of object ideally should present a dict-like interface and be able to preserve the formatting and comments of the original TOML file.

alias of MutableMapping[str, Any]

pyscaffold.toml.dumps(obj)[source]

Serialize a dict-like object into a TOML str, If the object was generated via loads, then the style will be preserved.

pyscaffold.toml.loads(text)[source]

Parse a string containing TOML into a dict-like object, preserving style somehow.

pyscaffold.toml.setdefault(obj, key, value)[source]

tomlkit seems to be tricky to use together with setdefault, this function is a workaroud for that.

When key is string containing '.', it will perform a nested setdefault.

pyscaffold.update module

Functionality to update one PyScaffold version to another

pyscaffold.update.ALWAYS = <VersionUpdate.ALWAYS: 1>

Perform the update action regardless of the version

pyscaffold.update.add_dependencies(setupcfg, opts)[source]

Add dependencies

pyscaffold.update.add_entrypoints(setupcfg, opts)[source]

Add [options.entry_points] to setup.cfg

pyscaffold.update.handover_setup_requires(setupcfg, opts)[source]

When paired with update_pyproject_toml, this will transfer setup.cfg :: options.setup_requires to pyproject.toml :: build-system.requires

pyscaffold.update.replace_find_with_find_namespace(setupcfg, opts)[source]
pyscaffold.update.update_pyproject_toml(struct, opts)[source]

Update old pyproject.toml generated by pyscaffoldext-pyproject and import setup_requires from update_setup_cfg into build-system.requires.

pyscaffold.update.update_setup_cfg(setupcfg, opts)[source]

Update pyscaffold in setupcfg and ensure some values are there as expected

pyscaffold.update.version_migration(struct, opts)[source]

Update projects that were generated with old versions of PyScaffold

Module contents