pyscaffold package¶
Subpackages¶
- pyscaffold.extensions package
- Submodules
- pyscaffold.extensions.cirrus module
- pyscaffold.extensions.config module
- pyscaffold.extensions.github_actions module
- pyscaffold.extensions.gitlab_ci module
- pyscaffold.extensions.interactive module
- pyscaffold.extensions.namespace module
- pyscaffold.extensions.no_pyproject module
- pyscaffold.extensions.no_skeleton module
- pyscaffold.extensions.no_tox module
- pyscaffold.extensions.pre_commit module
- pyscaffold.extensions.venv module
- Module contents
- pyscaffold.templates 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
,str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
|Tuple
[str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
,Callable
[[Path
,str
|None
,Dict
[str
,Any
]],Path
|None
]] |dict
],Dict
[str
,Any
]],Tuple
[Dict
[str
,str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
|Tuple
[str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
,Callable
[[Path
,str
|None
,Dict
[str
,Any
]],Path
|None
]] |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
andScaffoldOpts
.alias of
Tuple
[Dict
[str
,str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
|Tuple
[str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
,Callable
[[Path
,str
|None
,Dict
[str
,Any
]],Path
|None
]] |dict
],Dict
[str
,Any
]]
- pyscaffold.actions.DEFAULT = [<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
.
- 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
orafter
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.unregister(actions, reference)[source]¶
Prevent a specific action to be executed during scaffold.
- Parameters:
- 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': '4.6.post1.dev2+g157223e'}¶
Default values for PyScaffold’s options.
Options that can be derived from the values of other options (e.g.
package
can be derived fromproject_path
when not explicitly passed) are computed inpyscaffold.actions.get_default_options
.When
config_files
is empty, a default value is computed dynamically bypyscaffold.info.config_file
before the start of PyScaffold’s action pipeline.Warning
Default values might be dynamically overwritten by
config_files
or, during updates, existingsetup.cfg
.
- pyscaffold.api.NO_CONFIG = ConfigFiles.NO_CONFIG¶
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:
Valid options include:
- Project Information:
project_path (
os.PathLike
orstr
)
- Naming:
name (str): as in
pip install
or in PyPIpackage (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 isTrue
, PyScaffold will consider that some files can be updated (usually the packaging boilerplate), but will keep others intact. When the pretend flag isTrue
, 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 passconfig_file=NO_CONFIG
. See usage 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 thenamespace
extension define anamespace
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.dependencies module¶
Internal library for manipulating package dependencies and requirements.
- pyscaffold.dependencies.BUILD = ('setuptools_scm>=5',)¶
Dependencies that will be required to build the created project
- pyscaffold.dependencies.ISOLATED = ('setuptools>=46.1.0', 'setuptools_scm[toml]>=5')¶
Dependencies for isolated builds (PEP 517/PEP 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',))[source]¶
Given a sequence of individual requirement strings, add
to_add
to it. By default addsBUILD
ifto_add
is not given.
- pyscaffold.dependencies.attempt_pkg_name(requirement)[source]¶
In the case the given string is a dependency specification (PEP 508/ :pep`440`), it returns the “package name” part of dependency (without versions). Otherwise, it returns the same string (removed the comment marks).
- pyscaffold.dependencies.deduplicate(requirements)[source]¶
Given a sequence of individual requirement strings, e.g.
["platformdirs>=1.4.4", "packaging>20.0"]
, remove the duplicated packages. If a package is duplicated, the last occurrence stays.
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.DirectErrorForUser(message=None, *args, **kwargs)[source]¶
Bases:
RuntimeError
Parent class for exceptions that can be shown directly as error messages to the final users.
- exception pyscaffold.exceptions.DirectoryAlreadyExists(message=None, *args, **kwargs)[source]¶
Bases:
DirectErrorForUser
The project directory already exists, but no
update
orforce
option was used.
- exception pyscaffold.exceptions.DirectoryDoesNotExist(message=None, *args, **kwargs)[source]¶
Bases:
DirectErrorForUser
No directory was found to be updated.
- exception pyscaffold.exceptions.ErrorLoadingExtension(extension='', entry_point=None)[source]¶
Bases:
DirectErrorForUser
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.ExtensionNotFound(extensions)[source]¶
Bases:
DirectErrorForUser
The following extensions were not found: {extensions}. Please make sure you have the required versions installed. You can look for official extensions at https://github.com/pyscaffold.
- exception pyscaffold.exceptions.GitDirtyWorkspace(message="Your working tree is dirty. Commit your changes first or use '--force'.", *args, **kwargs)[source]¶
Bases:
DirectErrorForUser
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:
DirectErrorForUser
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. Use flag --very-verbose for more information.', *args, **kwargs)[source]¶
Bases:
DirectErrorForUser
PyScaffold requires git to run.
- DEFAULT_MESSAGE = 'Make sure git is installed and working. Use flag --very-verbose for more information.'¶
- exception pyscaffold.exceptions.ImpossibleToFindConfigDir(message=None, *args, **kwargs)[source]¶
Bases:
DirectErrorForUser
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.NestedRepository(directory)[source]¶
Bases:
DirectErrorForUser
The directory ‘{directory}’ is already part of a git repository.
PyScaffold avoids creating nested projects to prevent errors with setuptools-scm.
If you know what you are doing you can try running putup again with the`–force` flag, but please be aware that you will have to manually customise the configuration for setuptools-scm. For more information, have a look on:
- exception pyscaffold.exceptions.NoPyScaffoldProject(message='Could not update project. Was it generated with PyScaffold?', *args, **kwargs)[source]¶
Bases:
DirectErrorForUser
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:
DirectErrorForUser
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.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:
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.
- 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.
- 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
- 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)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)
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.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, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]¶
Bases:
Enum
- 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¶
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)[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)[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.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_workspace_clean(path)[source]¶
Checks if git workspace is clean
- Parameters:
path –
path to git repository
- Raises:
GitNotInstalled
: when git command is not availableGitNotConfigured
: 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 topath
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 topath
to read (default: setup.cfg)
- Returns:
Object that can be used to read/edit configuration parameters.
pyscaffold.log module¶
Custom logging infrastructure to provide execution information for the user.
- class pyscaffold.log.ColoredReportFormatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None)[source]¶
Bases:
ReportFormatter
Format logs with ANSI colors.
- ACTIVITY_STYLES = {'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 = {'critical': ('red', 'bold'), 'debug': ('green',), 'error': ('red',), 'info': ('blue',), 'warning': ('yellow',)}¶
- SUBJECT_STYLES = {'invoke': ('blue',)}¶
- TARGET_PREFIX = '\x1b[35m\x1b[1mto\x1b[0m'¶
- 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, *, defaults=None)[source]¶
Bases:
Formatter
Formatter that understands custom fields in the log record.
- ACTIVITY_MAXLEN = 12¶
- CONTEXT_PREFIX = 'from'¶
- SPACING = ' '¶
- TARGET_PREFIX = 'to'¶
- class pyscaffold.log.ReportLogger(logger=None, handler=None, formatter=None, extra=None, propagate=False)[source]¶
Bases:
LoggerAdapter
Suitable wrapper for PyScaffold CLI interactive execution reports.
- Parameters:
logger (logging.Logger) – custom logger to be used. Optional: the default logger will be used.
handlers (logging.Handler) – custom logging handler to be used. Optional: a
logging.StreamHandler
is used by default.formatter (logging.Formatter) – custom formatter to be used. Optional: by default a
ReportFormatter
is created and used.extra (dict) – extra attributes to be merged into the log record. Options, empty by default.
propagate (bool) – whether or not to propagate messages in the logging hierarchy,
False
by default. Seelogging.Logger.propagate
.
- 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
- 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 below
- 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
. Seelogging
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 ofcreate
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).
- 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 returnNone
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
FileContents
andScaffoldOpts
for other conventions.alias of
Callable
[[Path
,str
|None
,Dict
[str
,Any
]],Path
|None
]
- 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 isTrue
, no operation should be really performed, but any action should be logged as if realized.
- 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 (seeos.chmod
).file_op – a
FileOp
that will be “decorated”. If the file exists in disk afterfile_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).
- pyscaffold.operations.remove(path, _content, opts)[source]¶
Remove the file if it exists in the disk
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:
- 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.shell module¶
Shell commands like git, django-admin etc.
- pyscaffold.shell.EDITORS = {'atom': ['--wait'], 'code': ['--wait'], 'mate': ['-w'], 'nano': [], 'notepad': [], 'nvim': [], 'sensible-editor': [], 'subl': ['-w'], 'vi': [], 'vim': []}¶
Programs to be tried (in sequence) when calling
edit
andget_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 (
True
by default).cwd – current working dir to run the command
The produced command can be called with the following keyword arguments:
pretend (bool): skip execution (but log) when pretending.
False
by default.
The positional arguments are passed to the underlying shell command. In the case the path to the executable contains spaces of any other special shell character,
command
needs to be properly quoted.- 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/latest', include_path=True, shell=True, **kwargs)[source]¶
Similar to
get_executable
but return an instance ofShellCommand
if it is there to be found. Additional kwargs will be passed to theShellCommand
constructor.
- pyscaffold.shell.get_executable(name, prefix='/home/docs/checkouts/readthedocs.org/user_builds/pyscaffold/envs/latest', 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 addition to $PATH depending on the value of
include_path
. Defaults tosys.prefix
.include_path – when True the functions tries to look in the entire $PATH.
Note
The return value might contain whitespaces. If this value is used in a shell environment, it needs to be quote properly to avoid the underlying shell interpreter splitting the executable 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.join(parts)[source]¶
Join different parts of a shell command into a string, quoting whitespaces.
- pyscaffold.shell.python = <pyscaffold.shell.ShellCommand object>¶
Command for python
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
str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
- pyscaffold.structure.ActionParams¶
See
pyscaffold.actions.ActionParams
alias of
Tuple
[Dict
[str
,str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
|Tuple
[str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
,Callable
[[Path
,str
|None
,Dict
[str
,Any
]],Path
|None
]] |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
str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
|Tuple
[str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
,Callable
[[Path
,str
|None
,Dict
[str
,Any
]],Path
|None
]]
- pyscaffold.structure.Node¶
Representation of a file system node in the project structure (e.g. files, directories:
Union[Leaf, Structure]
alias of
str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
|Tuple
[str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
,Callable
[[Path
,str
|None
,Dict
[str
,Any
]],Path
|None
]] |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
[str
|None
,Callable
[[Path
,str
|None
,Dict
[str
,Any
]],Path
|None
]]
- 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
[str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
,Callable
[[Path
,str
|None
,Dict
[str
,Any
]],Path
|None
]]
- 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
andcallable
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 apyscaffold.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 withPyScaffold'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 contentprint("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
,str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
|Tuple
[str
|None
|Callable
[[Dict
[str
,Any
]],str
|None
] |Template
,Callable
[[Path
,str
|None
,Dict
[str
,Any
]],Path
|None
]] |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
andcallable
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 ifNone
.file_op – see
pyscaffold.operations
,create`
by default.
Changed in version 4.0: Instead of a
update_rule
flag, the function now accepts afile_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 aritymodifier
was changed to accept 2 arguments instead of only 1. This is more suitable to handling the newpyscaffold.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 sure content is string (calling
__call__()
orsafe_substitute()
with opts if necessary)
- pyscaffold.structure.reify_leaf(contents, opts)[source]¶
Similar to
resolve_leaf
but appliesreify_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.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
fordecorate
- 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:
- pyscaffold.termui.init_colorama()[source]¶
Initialize colorama if it is available.
- Returns:
result of check
- Return type:
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).
tomlkit is preferred because it supports comments.
- class 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
- 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.update module¶
Functionality to update one PyScaffold version to another
- pyscaffold.update.ALWAYS = VersionUpdate.ALWAYS¶
Perform the update action regardless of the version
- pyscaffold.update.handover_setup_requires(setupcfg, opts)[source]¶
When paired with
update_pyproject_toml
, this will transfersetup.cfg :: options.setup_requires
topyproject.toml :: build-system.requires
- 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.