Skip to content

Commit

Permalink
Merge pull request cookiecutter#1679 from cookiecutter/pyupgrade
Browse files Browse the repository at this point in the history
housekeeping: run pyupgrade to find remaining py27 artifacts
  • Loading branch information
ericof committed May 30, 2022
2 parents 8519c8d + 9ee72e1 commit 4ec79f7
Show file tree
Hide file tree
Showing 23 changed files with 83 additions and 95 deletions.
10 changes: 5 additions & 5 deletions cookiecutter/cli.py
Expand Up @@ -63,9 +63,9 @@ def list_installed_templates(default_config, passed_config_file):
os.path.join(cookiecutter_folder, folder, 'cookiecutter.json')
)
]
click.echo('{} installed templates: '.format(len(template_names)))
click.echo(f'{len(template_names)} installed templates: ')
for name in template_names:
click.echo(' * {}'.format(name))
click.echo(f' * {name}')


@click.command(context_settings=dict(help_option_names=['-h', '--help']))
Expand Down Expand Up @@ -219,11 +219,11 @@ def main(
click.echo(e)
sys.exit(1)
except UndefinedVariableInTemplate as undefined_err:
click.echo('{}'.format(undefined_err.message))
click.echo('Error message: {}'.format(undefined_err.error.message))
click.echo(f'{undefined_err.message}')
click.echo(f'Error message: {undefined_err.error.message}')

context_str = json.dumps(undefined_err.context, indent=4, sort_keys=True)
click.echo('Context: {}'.format(context_str))
click.echo(f'Context: {context_str}')
sys.exit(1)


Expand Down
6 changes: 2 additions & 4 deletions cookiecutter/config.py
Expand Up @@ -55,17 +55,15 @@ def merge_configs(default, overwrite):
def get_config(config_path):
"""Retrieve the config from the specified path, returning a config dict."""
if not os.path.exists(config_path):
raise ConfigDoesNotExistException(
'Config file {} does not exist.'.format(config_path)
)
raise ConfigDoesNotExistException(f'Config file {config_path} does not exist.')

logger.debug('config_path is %s', config_path)
with open(config_path, encoding='utf-8') as file_handle:
try:
yaml_dict = yaml.safe_load(file_handle)
except yaml.YAMLError as e:
raise InvalidConfiguration(
'Unable to parse YAML file {}.'.format(config_path)
f'Unable to parse YAML file {config_path}.'
) from e

config_dict = merge_configs(DEFAULT_CONFIG, yaml_dict)
Expand Down
8 changes: 4 additions & 4 deletions cookiecutter/environment.py
Expand Up @@ -4,7 +4,7 @@
from cookiecutter.exceptions import UnknownExtension


class ExtensionLoaderMixin(object):
class ExtensionLoaderMixin:
"""Mixin providing sane loading of extensions specified in a given context.
The context is being extracted from the keyword arguments before calling
Expand Down Expand Up @@ -32,9 +32,9 @@ def __init__(self, **kwargs):
extensions = default_extensions + self._read_extensions(context)

try:
super(ExtensionLoaderMixin, self).__init__(extensions=extensions, **kwargs)
super().__init__(extensions=extensions, **kwargs)
except ImportError as err:
raise UnknownExtension('Unable to load extension: {}'.format(err))
raise UnknownExtension(f'Unable to load extension: {err}')

def _read_extensions(self, context):
"""Return list of extensions as str to be passed on to the Jinja2 env.
Expand Down Expand Up @@ -62,4 +62,4 @@ def __init__(self, **kwargs):
Also loading extensions defined in cookiecutter.json's _extensions key.
"""
super(StrictEnvironment, self).__init__(undefined=StrictUndefined, **kwargs)
super().__init__(undefined=StrictUndefined, **kwargs)
8 changes: 4 additions & 4 deletions cookiecutter/exceptions.py
Expand Up @@ -124,10 +124,10 @@ def __init__(self, message, error, context):
def __str__(self):
"""Text representation of UndefinedVariableInTemplate."""
return (
"{self.message}. "
"Error message: {self.error.message}. "
"Context: {self.context}"
).format(**locals())
f"{self.message}. "
f"Error message: {self.error.message}. "
f"Context: {self.context}"
)


class UnknownExtension(CookiecutterException):
Expand Down
8 changes: 4 additions & 4 deletions cookiecutter/extensions.py
Expand Up @@ -13,7 +13,7 @@ class JsonifyExtension(Extension):

def __init__(self, environment):
"""Initialize the extension with the given environment."""
super(JsonifyExtension, self).__init__(environment)
super().__init__(environment)

def jsonify(obj):
return json.dumps(obj, sort_keys=True, indent=4)
Expand All @@ -26,7 +26,7 @@ class RandomStringExtension(Extension):

def __init__(self, environment):
"""Jinja2 Extension Constructor."""
super(RandomStringExtension, self).__init__(environment)
super().__init__(environment)

def random_ascii_string(length, punctuation=False):
if punctuation:
Expand All @@ -43,7 +43,7 @@ class SlugifyExtension(Extension):

def __init__(self, environment):
"""Jinja2 Extension constructor."""
super(SlugifyExtension, self).__init__(environment)
super().__init__(environment)

def slugify(value, **kwargs):
"""Slugifies the value."""
Expand All @@ -57,7 +57,7 @@ class UUIDExtension(Extension):

def __init__(self, environment):
"""Jinja2 Extension constructor."""
super(UUIDExtension, self).__init__(environment)
super().__init__(environment)

def uuid4():
"""Generate UUID4."""
Expand Down
14 changes: 7 additions & 7 deletions cookiecutter/generate.py
Expand Up @@ -96,8 +96,8 @@ def generate_context(
full_fpath = os.path.abspath(context_file)
json_exc_message = str(e)
our_exc_message = (
'JSON decoding error while loading "{0}". Decoding'
' error details: "{1}"'.format(full_fpath, json_exc_message)
'JSON decoding error while loading "{}". Decoding'
' error details: "{}"'.format(full_fpath, json_exc_message)
)
raise ContextDecodingException(our_exc_message)

Expand Down Expand Up @@ -180,7 +180,7 @@ def generate_file(project_dir, infile, context, env, skip_if_file_exists=False):

# Detect original file newline to output the rendered file
# note: newline='' ensures newlines are not converted
with open(infile, 'r', encoding='utf-8', newline='') as rd:
with open(infile, encoding='utf-8', newline='') as rd:
rd.readline() # Read the first line to load 'newlines' value

# Use `_new_lines` overwrite from context, if configured.
Expand Down Expand Up @@ -219,7 +219,7 @@ def render_and_create_dir(
'Output directory %s already exists, overwriting it', dir_to_create
)
else:
msg = 'Error: "{}" directory already exists'.format(dir_to_create)
msg = f'Error: "{dir_to_create}" directory already exists'
raise OutputDirExistsException(msg)
else:
make_sure_path_exists(dir_to_create)
Expand Down Expand Up @@ -292,7 +292,7 @@ def generate_files(
unrendered_dir, context, output_dir, env, overwrite_if_exists
)
except UndefinedError as err:
msg = "Unable to create project directory '{}'".format(unrendered_dir)
msg = f"Unable to create project directory '{unrendered_dir}'"
raise UndefinedVariableInTemplate(msg, err, context)

# We want the Jinja path and the OS paths to match. Consequently, we'll:
Expand Down Expand Up @@ -354,7 +354,7 @@ def generate_files(
if delete_project_on_failure:
rmtree(project_dir)
_dir = os.path.relpath(unrendered_dir, output_dir)
msg = "Unable to create directory '{}'".format(_dir)
msg = f"Unable to create directory '{_dir}'"
raise UndefinedVariableInTemplate(msg, err, context)

for f in files:
Expand All @@ -376,7 +376,7 @@ def generate_files(
except UndefinedError as err:
if delete_project_on_failure:
rmtree(project_dir)
msg = "Unable to create file '{}'".format(infile)
msg = f"Unable to create file '{infile}'"
raise UndefinedVariableInTemplate(msg, err, context)

if accept_hooks:
Expand Down
6 changes: 3 additions & 3 deletions cookiecutter/hooks.py
Expand Up @@ -83,14 +83,14 @@ def run_script(script_path, cwd='.'):
exit_status = proc.wait()
if exit_status != EXIT_SUCCESS:
raise FailedHookException(
'Hook script failed (exit status: {})'.format(exit_status)
f'Hook script failed (exit status: {exit_status})'
)
except OSError as os_error:
if os_error.errno == errno.ENOEXEC:
raise FailedHookException(
'Hook script failed, might be an empty file or missing a shebang'
)
raise FailedHookException('Hook script failed (error: {})'.format(os_error))
raise FailedHookException(f'Hook script failed (error: {os_error})')


def run_script_with_context(script_path, cwd, context):
Expand All @@ -102,7 +102,7 @@ def run_script_with_context(script_path, cwd, context):
"""
_, extension = os.path.splitext(script_path)

with open(script_path, 'r', encoding='utf-8') as file:
with open(script_path, encoding='utf-8') as file:
contents = file.read()

with tempfile.NamedTemporaryFile(delete=False, mode='wb', suffix=extension) as temp:
Expand Down
10 changes: 4 additions & 6 deletions cookiecutter/prompt.py
Expand Up @@ -58,16 +58,14 @@ def read_user_choice(var_name, options):
if not options:
raise ValueError

choice_map = OrderedDict(
('{}'.format(i), value) for i, value in enumerate(options, 1)
)
choice_map = OrderedDict((f'{i}', value) for i, value in enumerate(options, 1))
choices = choice_map.keys()
default = '1'

choice_lines = ['{} - {}'.format(*c) for c in choice_map.items()]
prompt = '\n'.join(
(
'Select {}:'.format(var_name),
f'Select {var_name}:',
'\n'.join(choice_lines),
'Choose from {}'.format(', '.join(choices)),
)
Expand Down Expand Up @@ -213,7 +211,7 @@ def prompt_for_config(context, no_input=False):

cookiecutter_dict[key] = val
except UndefinedError as err:
msg = "Unable to render variable '{}'".format(key)
msg = f"Unable to render variable '{key}'"
raise UndefinedVariableInTemplate(msg, err, context)

# Second pass; handle the dictionaries.
Expand All @@ -232,7 +230,7 @@ def prompt_for_config(context, no_input=False):

cookiecutter_dict[key] = val
except UndefinedError as err:
msg = "Unable to render variable '{}'".format(key)
msg = f"Unable to render variable '{key}'"
raise UndefinedVariableInTemplate(msg, err, context)

return cookiecutter_dict
6 changes: 3 additions & 3 deletions cookiecutter/replay.py
Expand Up @@ -12,14 +12,14 @@
def get_file_name(replay_dir, template_name):
"""Get the name of file."""
suffix = '.json' if not template_name.endswith('.json') else ''
file_name = '{}{}'.format(template_name, suffix)
file_name = f'{template_name}{suffix}'
return os.path.join(replay_dir, file_name)


def dump(replay_dir, template_name, context):
"""Write json data to file."""
if not make_sure_path_exists(replay_dir):
raise IOError('Unable to create replay dir at {}'.format(replay_dir))
raise OSError(f'Unable to create replay dir at {replay_dir}')

if not isinstance(template_name, str):
raise TypeError('Template name is required to be of type str')
Expand All @@ -43,7 +43,7 @@ def load(replay_dir, template_name):

replay_file = get_file_name(replay_dir, template_name)

with open(replay_file, 'r') as infile:
with open(replay_file) as infile:
context = json.load(infile)

if 'cookiecutter' not in context:
Expand Down
2 changes: 1 addition & 1 deletion cookiecutter/utils.py
Expand Up @@ -113,7 +113,7 @@ def simple_filter(filter_function):

class SimpleFilterExtension(Extension):
def __init__(self, environment):
super(SimpleFilterExtension, self).__init__(environment)
super().__init__(environment)
environment.filters[filter_function.__name__] = filter_function

SimpleFilterExtension.__name__ = filter_function.__name__
Expand Down
4 changes: 2 additions & 2 deletions cookiecutter/vcs.py
Expand Up @@ -73,7 +73,7 @@ def clone(repo_url, checkout=None, clone_to_dir='.', no_input=False):

# check that the appropriate VCS for the repo_type is installed
if not is_vcs_installed(repo_type):
msg = "'{0}' is not installed.".format(repo_type)
msg = f"'{repo_type}' is not installed."
raise VCSNotInstalled(msg)

repo_url = repo_url.rstrip('/')
Expand All @@ -83,7 +83,7 @@ def clone(repo_url, checkout=None, clone_to_dir='.', no_input=False):
repo_dir = os.path.normpath(os.path.join(clone_to_dir, repo_name))
if repo_type == 'hg':
repo_dir = os.path.normpath(os.path.join(clone_to_dir, repo_name))
logger.debug('repo_dir is {0}'.format(repo_dir))
logger.debug(f'repo_dir is {repo_dir}')

if os.path.isdir(repo_dir):
clone = prompt_and_delete(repo_dir, no_input=no_input)
Expand Down
4 changes: 2 additions & 2 deletions cookiecutter/zipfile.py
Expand Up @@ -55,7 +55,7 @@ def unzip(zip_uri, is_url, clone_to_dir='.', no_input=False, password=None):
zip_file = ZipFile(zip_path)

if len(zip_file.namelist()) == 0:
raise InvalidZipRepository('Zip repository {} is empty'.format(zip_uri))
raise InvalidZipRepository(f'Zip repository {zip_uri} is empty')

# The first record in the zipfile should be the directory entry for
# the archive. If it isn't a directory, there's a problem.
Expand Down Expand Up @@ -106,7 +106,7 @@ def unzip(zip_uri, is_url, clone_to_dir='.', no_input=False, password=None):

except BadZipFile:
raise InvalidZipRepository(
'Zip repository {} is not a valid zip archive:'.format(zip_uri)
f'Zip repository {zip_uri} is not a valid zip archive:'
)

return unzip_path
2 changes: 1 addition & 1 deletion tests/replay/test_dump.py
Expand Up @@ -16,7 +16,7 @@ def template_name():
@pytest.fixture
def replay_file(replay_test_dir, template_name):
"""Fixture to return a actual file name of the dump."""
file_name = '{}.json'.format(template_name)
file_name = f'{template_name}.json'
return os.path.join(replay_test_dir, file_name)


Expand Down
2 changes: 1 addition & 1 deletion tests/replay/test_load.py
Expand Up @@ -16,7 +16,7 @@ def template_name():
@pytest.fixture
def replay_file(replay_test_dir, template_name):
"""Fixture to return a actual file name of the dump."""
file_name = '{}.json'.format(template_name)
file_name = f'{template_name}.json'
return os.path.join(replay_test_dir, file_name)


Expand Down
6 changes: 3 additions & 3 deletions tests/test-extensions/hello_extension/hello_extension.py
Expand Up @@ -6,15 +6,15 @@
class HelloExtension(Extension):
"""Simple jinja2 extension for cookiecutter test purposes."""

tags = set(['hello'])
tags = {'hello'}

def __init__(self, environment):
"""Hello Extension Constructor."""
super(HelloExtension, self).__init__(environment)
super().__init__(environment)

def _hello(self, name):
"""Do actual tag replace when invoked by parser."""
return 'Hello {name}!'.format(name=name)
return f'Hello {name}!'

def parse(self, parser):
"""Work when something match `tags` variable."""
Expand Down
@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-

"""Provides custom extension, exposing a ``foobar`` filter."""

from jinja2.ext import Extension
Expand All @@ -11,7 +9,7 @@ class FoobarExtension(Extension):

def __init__(self, environment):
"""Foobar Extension Constructor."""
super(FoobarExtension, self).__init__(environment)
super().__init__(environment)
environment.filters['foobar'] = lambda v: v * 2


Expand Down
4 changes: 2 additions & 2 deletions tests/test_default_extensions.py
Expand Up @@ -25,7 +25,7 @@ def test_jinja2_time_extension(tmp_path):
changelog_file = os.path.join(project_dir, 'HISTORY.rst')
assert os.path.isfile(changelog_file)

with open(changelog_file, 'r', encoding='utf-8') as f:
with open(changelog_file, encoding='utf-8') as f:
changelog_lines = f.readlines()

expected_lines = [
Expand Down Expand Up @@ -57,7 +57,7 @@ def test_jinja2_uuid_extension(tmp_path):
changelog_file = os.path.join(project_dir, 'id')
assert os.path.isfile(changelog_file)

with open(changelog_file, 'r', encoding='utf-8') as f:
with open(changelog_file, encoding='utf-8') as f:
changelog_lines = f.readlines()

uuid.UUID(changelog_lines[0], version=4)
Expand Down

0 comments on commit 4ec79f7

Please sign in to comment.