# -*- coding: utf-8 -*-
"""
Provide general information about the system, user etc.
"""
import copy
import getpass
import os
import socket
from enum import Enum
from operator import itemgetter
from . import shell
from .exceptions import (
GitNotConfigured,
GitNotInstalled,
PyScaffoldTooOld,
ShellCommandException,
)
from .templates import licenses
from .update import read_setupcfg
from .utils import chdir, levenshtein
[docs]class GitEnv(Enum):
author_name = "GIT_AUTHOR_NAME"
author_email = "GIT_AUTHOR_EMAIL"
author_date = "GIT_AUTHOR_DATE"
committer_name = "GIT_COMMITTER_NAME"
committer_email = "GIT_COMMITTER_EMAIL"
committer_date = "GIT_COMMITTER_DATE"
[docs]def username():
"""Retrieve the user's name
Returns:
str: user's name
"""
user = os.getenv(GitEnv.author_name.value)
if user is None:
try:
user = next(shell.git("config", "--get", "user.name"))
user = user.strip()
except ShellCommandException:
user = getpass.getuser()
return user
[docs]def email():
"""Retrieve the user's email
Returns:
str: user's email
"""
mail = os.getenv(GitEnv.author_email.value)
if mail is None:
try:
mail = next(shell.git("config", "--get", "user.email"))
mail = mail.strip()
except ShellCommandException:
user = getpass.getuser()
host = socket.gethostname()
mail = "{user}@{host}".format(user=user, host=host)
return mail
[docs]def is_git_installed():
"""Check if git is installed
Returns:
bool: True if git is installed, False otherwise
"""
if shell.git is None:
return False
try:
shell.git("--version")
except ShellCommandException:
return False
return True
[docs]def check_git():
"""Checks for git and raises appropriate exception if not
Raises:
:class:`~.GitNotInstalled`: when git command is not available
:class:`~.GitNotConfigured`: when git does not know user information
"""
if not is_git_installed():
raise GitNotInstalled
if not is_git_configured():
raise GitNotConfigured
[docs]def is_git_workspace_clean(path):
"""Checks if git workspace is clean
Args:
path (str): path to git repository
Returns:
bool: condition if workspace is clean or not
Raises:
:class:`~.GitNotInstalled`: when git command is not available
:class:`~.GitNotConfigured`: when git does not know user information
"""
# ToDo: Change to pathlib for v4
check_git()
try:
with chdir(path):
shell.git("diff-index", "--quiet", "HEAD", "--")
except ShellCommandException:
return False
return True
[docs]def project(opts):
"""Update user options with the options of an existing PyScaffold project
Params:
opts (dict): options of the project
Returns:
dict: options with updated values
Raises:
:class:`~.PyScaffoldTooOld`: when PyScaffold is to old to update from
:class:`~.NoPyScaffoldProject`: when project was not generated with
PyScaffold
"""
from pkg_resources import iter_entry_points
opts = copy.deepcopy(opts)
cfg = read_setupcfg(opts["project"]).to_dict()
if "pyscaffold" not in cfg:
raise PyScaffoldTooOld
pyscaffold = cfg["pyscaffold"]
metadata = cfg["metadata"]
# This would be needed in case of inplace updates, see issue #138, v4
# if opts['project'] == '.':
# opts['project'] = metadata['name']
# Overwrite only if user has not provided corresponding cli argument
opts.setdefault("package", pyscaffold["package"])
opts.setdefault("author", metadata["author"])
opts.setdefault("email", metadata["author-email"])
opts.setdefault("url", metadata["url"])
opts.setdefault("description", metadata["description"])
opts.setdefault("license", best_fit_license(metadata["license"]))
# Additional parameters compare with `get_default_options`
opts["classifiers"] = metadata["classifiers"].strip().split("\n")
# complement the cli extensions with the ones from configuration
if "extensions" in pyscaffold:
cfg_extensions = pyscaffold["extensions"].strip().split("\n")
opt_extensions = [ext.name for ext in opts["extensions"]]
add_extensions = set(cfg_extensions) - set(opt_extensions)
for extension in iter_entry_points("pyscaffold.cli"):
if extension.name in add_extensions:
extension_obj = extension.load()(extension.name)
if extension.name in pyscaffold:
ext_value = pyscaffold[extension.name]
extension_obj.args = ext_value
opts[extension.name] = ext_value
opts["extensions"].append(extension_obj)
return opts
[docs]def best_fit_license(txt):
"""Finds proper license name for the license defined in txt
Args:
txt (str): license name
Returns:
str: license name
"""
ratings = {lic: levenshtein(txt, lic.lower()) for lic in licenses}
return min(ratings.items(), key=itemgetter(1))[0]